使用mysql-python处理大型数据集时存在内存泄漏问题

10

在使用MySQLdb API时,我认为我遇到了内存泄漏的问题。

Line #    Mem usage    Increment   Line Contents
================================================
     6                             @profile
     7    10.102 MB     0.000 MB   def main():
     8    10.105 MB     0.004 MB       connection = MySQLdb.connect(host="localhost", db="mydb",
     9    11.285 MB     1.180 MB                                    user="notroot", passwd="Admin123", use_unicode=True)
    10    11.285 MB     0.000 MB       cursor = connection.cursor(cursorclass=MySQLdb.cursors.SSCursor)
    11                                 
    12    11.289 MB     0.004 MB       cursor.execute("select * from a big table;")
    13                                 
    14   254.078 MB   242.789 MB       results = [result for result in cursor]
    15   251.672 MB    -2.406 MB       del results
    16   251.672 MB     0.000 MB       return

同时,当使用guppy/hpy探索堆时,显示我的大部分内存被unicode对象、整数和日期时间对象占用(很可能是由MySQLdb API返回的行)。

我正在Ubuntu 12.04上使用Python 2.7.3和mysql-python==1.2.4,并使用memory_profiler进行分析。

这可能是像http://effbot.org/pyfaq/why-doesnt-python-release-the-memory-when-i-delete-a-large-object.htm中所描述的那样缓存吗?

我有没有漏掉任何悬空引用?

编辑:我也关闭了光标和连接,但仍然得到类似的结果。

已解决: 脸掌。我正在使用列表推导式,这会在内存中保留所有内容。当正确使用迭代器(流式传输到文件或其他地方)时,它具有合理的内存使用率。

Line #    Mem usage    Increment   Line Contents
================================================
    16                             @profile
    17    10.055 MB     0.000 MB   def main():
    18    10.059 MB     0.004 MB       connection = MySQLdb.connect(host="localhost", db="mydb",
    19    11.242 MB     1.184 MB                                    user="notroot", passwd="Admin123", use_unicode=True)
    20    11.242 MB     0.000 MB       cursor = connection.cursor(cursorclass=MySQLdb.cursors.SSCursor)
    21                                 
    22    11.246 MB     0.004 MB       cursor.execute("select * from big table")
    23    11.246 MB     0.000 MB       count = 0
    24    30.887 MB    19.641 MB       for result in cursor:
    25    30.887 MB     0.000 MB           count = count + 1
    26    30.895 MB     0.008 MB       cursor.close()
    27    30.898 MB     0.004 MB       connection.close()
    28    30.898 MB     0.000 MB       return

2
此外,操作系统并不会立即释放内存。如果进程再次需要该内存,内存仍然保留给Python使用,只有在其他地方需要时才会从进程中移除。仅仅因为Python释放了内存,并不意味着操作系统会立即回收它。 - Martijn Pieters
我忘记关闭游标和连接,但即使这样做了,它仍然无法释放内存。 - Andrei Coman
5
循环运行这个程序最终会占用全部内存吗? - Martijn Pieters
2
@AndreiComan 不能保证进程释放的内存会被还给操作系统,因此 top/ps/memory_profiler 等工具显示进程的内存使用量减少也不是一定的。分配的地址空间反而会在进程中保留在一个“池”中。通常这不是问题,因为这是虚拟内存,操作系统最终会通过交换出未使用的物理内存来解决问题。这也不特定于 Python。但如果做类似的事情时内存不断增加,那可能表明存在问题。 - nos
9
很高兴您找到了解决问题的方法!您能否将其写成一个正式的答案,以便不再标记为未解决? - Hannele
显示剩余2条评论
1个回答

2

问题已由提问者解决。他原来的代码中包含了以下行:

results = [result for result in cursor]

这个列表推导式将整个结果存储在内存中,而不是根据需要从服务器流式传输。 OP将其替换为简单的方式。
for result in cursor:
    ...

并且看到他的内存使用率恢复正常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接