簡體   English   中英

大查詢后psycopg2泄漏內存

[英]psycopg2 leaking memory after large query

我正在使用psycopg2(我升級到2.5版)在我的postgres數據庫的python腳本中運行一個大型查詢。 查詢完成后,我關閉光標和連接,甚至運行gc,但進程仍然消耗大量內存(確切地說是7.3gb)。 我錯過了一個清理步驟嗎?

import psycopg2
conn = psycopg2.connect("dbname='dbname' user='user' host='host'")
cursor = conn.cursor()
cursor.execute("""large query""")
rows = cursor.fetchall()
del rows
cursor.close()
conn.close()
import gc
gc.collect()

我遇到了類似的問題,經過幾個小時的血,汗和淚,發現答案只需要添加一個參數。

代替

cursor = conn.cursor()

cursor = conn.cursor(name="my_cursor_name")

或者更簡單

cursor = conn.cursor("my_cursor_name")

有關詳細信息,請訪問http://initd.org/psycopg/docs/usage.html#server-side-cursors

我發現這些說明有點令人困惑,因為我需要重寫我的SQL以包含“DECLARE my_cursor_name ....”然后“FETCH count 2000 FROM my_cursor_name”但事實證明psycopg會為你完成這一切如果您只是在創建游標時覆蓋“name = None”默認參數。

上面使用fetchone或fetchmany的建議不能解決問題,因為如果你保留name參數unset,psycopg默認會嘗試將整個查詢加載到ram中。 您可能需要做的唯一其他事情(除了聲明一個名稱參數)是將cursor.itersize屬性從默認的2000更改為1000,如果您仍然有太少的內存。

請查看@joeblog的下一個答案 ,以獲得更好的解決方案。


首先,您不應該首先需要所有RAM。 你應該在這里做的是獲取結果集的 不要做一個fetchall() 相反,使用更有效的cursor.fetchmany方法。 請參閱psycopg2文檔

現在,解釋為什么它沒有被釋放,以及為什么這不是正式正確使用該術語時的內存泄漏。

大多數進程在釋放后不會將內存釋放回操作系統,只是讓它可以在程序的其他地方重用。

如果程序可以壓縮通過內存分散的剩余對象,則只能將內存釋放到OS。 這只有在使用間接句柄引用時才有可能,因為否則移動對象會使對象的現有指針無效。 間接引用的效率相當低,特別是在現代CPU中,追逐指針會對性能產生可怕的影響。

通常會發生什么事情,除非程序提供額外的謹慎,每個大塊的內存分配brk()與一些仍在使用的小塊。

操作系統無法判斷程序是否認為此內存仍在使用中,因此它不能僅僅聲明它。 由於程序不傾向於訪問內存,因此OS通常會隨着時間的推移將其交換掉,從而釋放物理內存用於其他用途。 這是您應該擁有交換空間的原因之一。

編寫將內存交還給操作系統的程序是可能的,但我不確定你是否可以用Python來實現。

也可以看看:

所以:這實際上不是內存泄漏 如果你做了一些使用大量內存的事情,那么這個過程不應該增長很多,如果有的話,它將重新使用上一個大分配中先前釋放的內存。

Joeblog有正確的答案。 處理提取的方式很重要,但比您必須定義光標的方式要明顯得多。 這是一個簡單的例子來說明這一點,並給你一些復制粘貼開始。

import datetime as dt
import psycopg2
import sys
import time

conPG = psycopg2.connect("dbname='myDearDB'")
curPG = conPG.cursor('testCursor')
curPG.itersize = 100000 # Rows fetched at one time from the server

curPG.execute("SELECT * FROM myBigTable LIMIT 10000000")
# Warning: curPG.rowcount == -1 ALWAYS !!
cptLigne = 0
for rec in curPG:
   cptLigne += 1
   if cptLigne % 10000 == 0:
      print('.', end='')
      sys.stdout.flush() # To see the progression
conPG.commit() # Also close the cursor
conPG.close()

正如您將看到的那樣,點組來得很快,而不是暫停以獲得行緩沖(itersize),因此您不需要使用fetchmany來提高性能。 當我使用/usr/bin/time -v運行它/usr/bin/time -v ,我在不到3分鍾的/usr/bin/time -v得到結果,僅使用200MB的RAM(而不是帶有客戶端游標的60GB)用於1000萬行。 服務器不需要更多內存,因為它使用臨時表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM