2014-03-26 33 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