流行的 python mysql 模块“MySQLdb”似乎存在内存泄漏问题。 这是代码:
conn = MySQLdb.connect(...)
cursor = conn.cursor(cursorclass = MySQLdb.cursors.DictCursor)
sql = "select * from `test`"
cursor.execute(sql) #leak start
cursor.close()
conn.close()
time.sleep(20)
假设
test
是一个有十亿条记录的表。我运行了 python 代码并执行
ps aux | awk '{print $2, $4, $11}' | grep python
同时,结果是内存使用增加到47.0%并且再也回不去了,即使我关闭游标和conn。有什么想法吗?
在这篇文章中,Fredrik Lundh 解释了为什么内存可能无法返回到系统,即使这不是内存泄漏。 在底部附近,他解释了为什么(在 Python2 中)
range(50*1024*100)
可能会消耗大量内存,即使在删除列表后也不会释放这些内存。他提到使用 xrange
是首先避免内存问题的一种方法。
同样,使用
SSDictCursor
而不是 DictCursor
可能是避免您的情况出现内存问题的一种方法。
SSDictCursor
导致 MySQL 服务器将结果集保留在服务器端,并且游标将仅根据需要一次从结果集中获取行:
import MySQLdb
import MySQLdb.cursors as cursors
conn = MySQLdb.connect(..., cursorclass=cursors.SSDictCursor) #1
cursor = conn.cursor()
cursor.execute('select * from test') #2
for row in cursor: #3
print(row)
conn.close()
cursorclass=cursors.SSDictCursor
以进行连接。DictCursor
(或任何非 SS 游标),对 execute
的调用将导致 MySQLdb
将整个结果集加载到 Python 对象中(例如字典列表)。SSDictCursor
,MySQLdb
一次检索一行。因此,如果您不需要在 Python 中一次性保存整个结果集,这将避免内存累积问题。
另请注意,使用
SSCursor
或 SSDictCursor
时,“在获取整个结果集之前,无法在连接上发出新查询。”可以同时使用来自两个不同连接的游标。这对您来说可能不是问题,但需要注意。
您可能还想查看 oursql,这是 MySQL 的替代数据库适配器。 oursql
游标是服务器端游标,默认情况下延迟获取。