2014-03-26 32 views
5

流行的python mysql模块“MySQLdb”似乎有内存泄漏问题。 下面的代码:Python 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%,从来没有回去,即使我关闭游标和康涅狄格州。有任何想法吗?

+1

http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm – unutbu

+0

我有同样的问题。多年来一直没有找到解决办法。相反,我只写了一个cronjob,当内存变高时重新启动服务器。这是一个黑客,但它的工作原理。 –

回答

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() 
  1. 注意cursorclass=cursors.SSDictCursor在通话中连接。
  2. 使用DictCursor(或任何非SS游标),此调用execute将导致MySQLdb到 将整个结果集加载到Python对象(例如一个字典列表)中。
  3. 使用SSDictCursor,MySQLdb一次检索一行。

因此,这样可以避免内存堆积问题,前提是您不需要一次全部保存整个结果集。

还要注意,当使用SSCursorSSDictCursor“不能在连接until the entire result set has been fetched.上发出新的查询”时,可以同时使用来自两个不同连接的游标。这对你来说可能不是问题,但它是需要注意的。

您可能还想查看oursql,这是MySQL的备用数据库适配器。 oursql游标是服务器端游标fetch lazily by default

+0

非常感谢。真的有帮助〜 –

+0

等一下,有没有办法在运行时手动释放系统的内存? –

+0

不幸的是,[释放内存的唯一可靠方法是终止进程](http://stackoverflow.com/questions/1316767/how-can-i-explicitly-free-memory-in-python/1316799#1316799) 。 – unutbu