MySQLdb カーソルをN件ずつフェッチする
↓ここを参考。
Django(正確にはMySQLdb)をつかってMySQLの巨大な結果を返すselect文を処理する
http://d.hatena.ne.jp/shohu33/20091122
↓こちらも。
http://www.ueblog.org/blog/entry/pythonmysqldb/
MySQLdbで大量のレコードをSELECTする場合、
cursor.execute & cursor.fetchoneを使うと処理が著しく遅くなる。
フェッチする件数が多いとmysqlが落ちたり、タイムアウトしてしまう。
※cursor.fetchallをすると巨大なリストをメモリに展開する問題とは違う。
どうも、
↑このうち、2に失敗しているようだ。理由はよくわからない。
DBが内部にカーソルを展開する時点で失敗するので始末が悪いのだが、
connection.query & connection.use_result() を使うとこの問題を解決できる。
for-inループで使いたかったので、__iter__を持つクラスを作ってみた。
# -*- coding: utf-8 -*- import MySQLdb def get_connection(connect_info): ''' 単純にMySQLのコネクションを作って返します。 ''' return MySQLdb.connect(**connect_info) class low_level_cursor(object): def __init__(self, connection, rows=1, value_type=1): self.connection = connection self.rows = rows self.value_type = value_type self.rs = None self.row = None def query(self, sql): self.connection.query(sql) self.rs = self.connection.use_result() def has_next(self): self.row = self.rs.fetch_row(self.rows, self.value_type) return bool(self.row) def next(self): return self.row[0] if self.rows == 1 else self.row def __iter__(self): while(self.has_next()): yield self.next() foo = { 'db' : "imagawa", 'host' : "127.0.0.1", 'port' : 3306, 'user' : "user", 'passwd' : "password" } connection = get_connection(foo) cursor = low_level_cursor(connection) cursor.query('select id, name, email from huge_data') for i in cursor: # do something...
以下、参考。
http://mysql-python.sourceforge.net/MySQLdb.html#using-and-extending