Django(正確にはMySQLdb)をつかってMySQLの巨大な結果を返すselect文を処理する
移転しました。
最近djangoでデータ件数が多いテーブルから
hoge = Hoge.objects.all()
のように取得しようとすると、本ケースのようにデータが多い場合メモリ不足で処理が落ちる事がある。
なんとか他の方法でデータ取得できないかと以下のようにこころみたが"killed"で同じように落ちてしまた。
from django.db import connection, transaction cursor = connection.cursor() cursor.execute("select * from hoge") killed
知り合いの方がJavaで同じような現象にはまったことを聞いて、どうやって解決したかを聞いてみると
JDBCのドライバ設定をかえて1行1行とるようにしたそうな。
※具体的なパラメータ名までは聞かなかった。。。
「Pythonにもあるんじゃないの?」
というアドバイスを頂いたので、調べていると案の定あるではないか。
DjangoではDB接続時にMySQLを使用する場合にMySQLdbを使用しているのだが、
こいつのuse_resultメソッドを使えば解決できそうなことがわかった。
解決した
ここ「PythonをつかってMySQLの巨大な結果を返すselect文を処理する」のソースを参考に以下のようなソースを作成して実行したところ、処理がメモリ不足で落ちる事なく最後まで実行できた。
from django.conf import settings from django.db import connection from prj.app.models import Hoge # MySQLdbのconnectionを取得 # 1.connection.connectionにMySQLdbのconnectionを入れるために実行 connection.cursor() conn = connection.connection # MySQLdb.connection を取得 # ユーザーデータを1行1行処理していく # 必ず使用する列のみの取得とすること!!!! # (保存する列数分のメモリが消費されるため) conn.query("select id, name from hoge") # 2.use_resultつかってcursor?取得 cur = conn.use_result() while(True): # row = cur.fetch_row(返す行数, 返す型(0:Tuple, 1:dict)) # デフォは 返す行数:1, 返す型:Tuple rows = cur.fetch_row(1,1) if not rows: break print rows
1.connection.connectionにMySQLdbのconnectionを入れる
djangoのconnection.connectionにMySQLdbのconnectionオブジェクトが入るのだが一度DBアクセスしないとconnectionに値が入らない模様。このため connection.cursor() してオブジェクトとってくる。
※もっとよい方法ないだろうか?
2.use_resultつかってcursor?取得
そのごuse_result使って欲しいcursor取得してループしてあげるだけ。
返す型はタプルか辞書かの指定ができる。
詳しくはここ参照
気をつけよう
サーバー側のメモリを使うため、あまり同時にuse_result使うようなPGは動かさない方が良い事に気をつける。