Python-MySQL(MySQLdb)のメモリリーク対策
10月 27th, 2009
会社のスクリプト言語がPythonに統一されて、今月からPythonをメインに使っているのですが、MySQLに接続する際なぜかものすごくメモリを食う時がありました。その備忘録。
最近技術ネタが続いていますがf^^;
結論から先に書くと、MySQLdbのfetchallの部分を下記のように書きなおすとメモリリークが起きなくなりました。
sql = "SQL文" con= MySQLdb.connect(db = db, host = host, user = user, passwd = passwd) cur = con.cursor() cur.execute(sql, params) result = cur.fetchall()
↓
sql = "SQL文" con = MySQLdb.connect(db = db, host = host, user = user, passwd = passwd) con.query(sql) r = con.store_result() # use_result()も可 result = [] while(True): row = r.fetch_row() if not row: break result.insert(0, row[0]) # 初めにデータを挿入してリストの再構成を防ぎ高速化! con.close()
» ueBLOG | PythonをつかってMySQLの巨大な結果を返すselect文を処理する
主にこちらのブログを参考にしたのですが、cursorを使わず直接connectからクエリを投げるようにしています。
大きな違いは、一度にデータを取得するのではなく、1行ずつ取得するようにしている、ということだと思います。6~9行目で取得する行を一つ一つリストに格納しています。
なお、4行目でstore_result()を使っていますが、use_result()も可能で、use_result()を使うとデータをサーバに保持しそこから一行ずつ取得するようになるので、よりメモリの消費が抑えられる、気がしますf^^;
確認していないので何とも言えないのですが。
SQLAlchemyだとメモリリークしない、と聞いて試してみたのですが、ORマッピングで導入が面倒くさそうなうえに、生SQLを叩く際は結局MySQLdbを使うようで問題は解決しませんでした。
大量のデータをMySQLに保存しそこから全文検索(エンジンはSenna)でデータを引っ張る処理を行っていて、ある特定のSQLを書く必要があったので。
参考:
» MySQLdbのメモリー・リーク – スコトプリゴニエフスク通信
» [Python] MySQLdbのメモリリーク (それなりブログ)
こんにちは
結果を全て配列に入れると、どんな方法をとってもメモリを食うので
メモリが気になるくらいのデータが戻ってくるなら
con= MySQLdb.connect(db = db, host = host, user = user, passwd = passwd)
cur = con.cursor()
cur.execute(sql, params)
for row in cur:
# 列に対する処理
# 例えば
print “row0: %s row1: %s ” % (row[0], row[1])
のようにします。
下の例でも配列に入れないで generatorで使うようにしています。
(下のコードでちゃんと動くか試してません!すみません!でも、こんな感じです)
def hoge(sql, db=db, host=host, user=user, passwd=passwd):
con = MySQLdb.connect(db = db, host = host, user = user, passwd = passwd)
con.query(sql)
r = con.use_result()
while(True):
row = r.fetch_row()
if not row:
raise StopIteration
yield row[0]
con.close()
def main():
result = hoge(”SELECT * FROM hoge”)
for row in result:
#rowに対する処理
#例えば
print “row0: %s row1: %s ” % (row[0], row[1])
すみません、レスが遅れてしまいました。
ご丁寧なアドバイスありがとうございます!
配列に入れない方法だとメモリを食わなそうですね。
来週試してみたいと思います。