会社のスクリプト言語が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のメモリリーク (それなりブログ)