流行的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。有什么想法吗?
答案 0 :(得分:8)
在this post中,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对象(例如dicts列表)中。SSDictCursor
,MySQLdb
一次检索一行。因此,如果您不需要一次性保存Python中的整个结果集,这将避免内存构建问题。
另请注意,使用SSCursor
或SSDictCursor
时,“无法在连接上发出新查询until the entire result set has been fetched.”可以同时使用来自两个不同连接的游标。这对你来说可能不是问题,但需要注意的是。
您可能还想查看oursql,这是MySQL的替代数据库适配器。 oursql
游标是服务器端游标fetch lazily by default。