Home > IT, proposal, エンジニア > Python-MySQL(MySQLdb)のメモリリーク対策

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のメモリリーク (それなりブログ)

Similar Posts:

8maki IT, proposal, エンジニア , , ,

  1. 11月 16th, 2009 at 10:34 | #1

    こんにちは

    結果を全て配列に入れると、どんな方法をとってもメモリを食うので
    メモリが気になるくらいのデータが戻ってくるなら

    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])

  2. 12月 12th, 2009 at 12:09 | #2

    すみません、レスが遅れてしまいました。

    ご丁寧なアドバイスありがとうございます!

    配列に入れない方法だとメモリを食わなそうですね。
    来週試してみたいと思います。

  1. No trackbacks yet.