默认情况下,MySQL ResultSet会在完成任何工作之前从服务器中完全检索.在巨大的结果集的情况下,这变得无法使用.我希望实际上从服务器中逐个检索行.
在Java中,按照这里的说明(在"ResultSet"下),我创建一个这样的语句:
stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY, java.sql.ResultSet.CONCUR_READ_ONLY); stmt.setFetchSize(Integer.MIN_VALUE);
这在Java中很有效.我的问题是:有没有办法在python中做同样的事情?
我试过的一件事是一次将查询限制为1000行,如下所示:
start_row = 0 while True: cursor = conn.cursor() cursor.execute("SELECT item FROM items LIMIT %d,1000" % start_row) rows = cursor.fetchall() if not rows: break start_row += 1000 # Do something with rows...
但是,较高的start_row似乎会变慢.
不,使用fetchone()
而fetchall()
不是改变任何东西.
我用来重现这个问题的天真代码如下所示:
import MySQLdb conn = MySQLdb.connect(user="user", passwd="password", db="mydb") cur = conn.cursor() print "Executing query" cur.execute("SELECT * FROM bigtable"); print "Starting loop" row = cur.fetchone() while row is not None: print ", ".join([str(c) for c in row]) row = cur.fetchone() cur.close() conn.close()
在约700,000行表上,此代码运行得很快.但是在一个~9,000,000行的表中,它打印出"执行查询",然后挂起很长时间.这就是为什么如果我使用fetchone()
或者它没有任何区别fetchall()
.
我想你必须连接传递cursorclass = MySQLdb.cursors.SSCursor
:
MySQLdb.connect(user="user", passwd="password", db="mydb", cursorclass = MySQLdb.cursors.SSCursor )
即使您不使用,默认光标也会立即获取所有数据fetchall
.
编辑:SSCursor
或支持服务器端结果集的任何其他游标类 - 检查模块文档MySQLdb.cursors
.
限制/偏移解决方案以二次方运行,因为mysql必须重新扫描行才能找到偏移量.如您所料,默认光标将整个结果集存储在客户端上,这可能会占用大量内存.
相反,您可以使用服务器端游标,它可以使查询保持运行并根据需要获取结果.可以通过向连接调用本身提供默认值,或者每次向游标方法提供一个类来自定义游标类.
from MySQLdb import cursors cursor = conn.cursor(cursors.SSCursor)
但这不是整个故事.除了存储mysql结果之外,默认的客户端游标实际上无论如何都会获取每一行.这种行为没有记录,非常不幸.这意味着为所有行创建了完整的python对象,这比原始的mysql结果消耗的内存要多得多.
在大多数情况下,存储在客户端上的结果作为迭代器包装将产生具有合理内存使用的最佳速度.但如果你愿意的话,你必须自己动手.
你试过这个版本的fetchone吗?或者不同的东西?
row = cursor.fetchone() while row is not None: # process row = cursor.fetchone()
还有,你试试这个吗?
row = cursor.fetchmany(size=1) while row is not None: # process row = cursor.fetchmany( size=1 )
并非所有驱动程序都支持这些,因此您可能遇到错误或发现它们太慢.
编辑.
当它挂起执行时,你正在等待数据库.这不是一个逐行的Python事物; 这是MySQL的事情.
MySQL更喜欢将所有行作为其自身缓存管理的一部分来获取.通过提供Integer.MIN_VALUE(-2147483648L)的fetch_size来关闭此功能.
问题是,Python DBAPI的哪一部分变成了JDBC fetch_size的等价物?
我认为它可能是游标的arraysize属性.尝试
cursor.arraysize=-2**31
并查看是否强制MySQL流式传输结果集而不是缓存它.