Ada*_*ine 18 python postgresql psycopg2
我正在使用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()
Run Code Online (Sandbox Code Playgroud)
joe*_*log 40
我遇到了类似的问题,经过几个小时的血,汗和泪,发现答案只需要添加一个参数.
代替
cursor = conn.cursor()
Run Code Online (Sandbox Code Playgroud)
写
cursor = conn.cursor(name="my_cursor_name")
Run Code Online (Sandbox Code Playgroud)
或者更简单
cursor = conn.cursor("my_cursor_name")
Run Code Online (Sandbox Code Playgroud)
有关详细信息,请访问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()
Run Code Online (Sandbox Code Playgroud)
正如您将看到的那样,点组来得很快,而不是暂停以获得行缓冲(itersize),因此您不需要使用fetchmany
性能.当我运行它时/usr/bin/time -v
,我在不到3分钟的时间内得到结果,仅使用200MB的RAM(而不是带有客户端光标的60GB),用于1000万行.服务器不需要更多内存,因为它使用临时表.
归档时间: |
|
查看次数: |
11373 次 |
最近记录: |