首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >大型查询后psycopg2内存泄漏

大型查询后psycopg2内存泄漏
EN

Stack Overflow用户
提问于 2013-06-20 02:55:56
回答 3查看 16.8K关注 0票数 26

我正在使用psycopg2在我的postgres数据库上运行python脚本中的大型查询(我升级到了2.5版)。查询完成后,我关闭游标和连接,甚至运行gc,但该过程仍然消耗大量内存(确切地说是7.3gb)。我是不是错过了一个清理步骤?

代码语言:javascript
运行
复制
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()
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2015-01-14 18:08:12

我遇到了类似的问题,在经历了几个小时的血汗和泪水之后,我发现答案只需要添加一个参数。

而不是

代码语言:javascript
运行
复制
cursor = conn.cursor()

代码语言:javascript
运行
复制
cursor = conn.cursor(name="my_cursor_name")

或者更简单

代码语言:javascript
运行
复制
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“,但事实证明,如果您在创建游标时只需覆盖"name=None”默认参数,就可以在幕后为您完成所有这些操作。

上面的使用fetchone或fetchmany的建议并没有解决这个问题,因为如果你没有设置name参数,entire会默认地尝试将整个查询加载到ram中。除了声明name参数之外,您可能还需要做的另一件事是,如果内存仍然太少,则将cursor.itersize属性从默认的2000更改为1000。

票数 57
EN

Stack Overflow用户

发布于 2013-06-20 09:20:53

请查看@joeblog的下一个答案以获得更好的解决方案。

首先,你不应该一开始就需要这么多的RAM。您在这里应该做的是获取结果集的区块。不要做fetchall()。取而代之的是,使用更有效的cursor.fetchmany方法。参见the psycopg2 documentation

现在,解释为什么它没有释放,以及为什么这不是正式正确使用该术语时的内存泄漏。

大多数进程在释放内存时不会将其释放回操作系统,它们只是让它可用于程序中其他地方的重用。

只有当程序可以压缩分散在内存中的剩余对象时,才能将内存释放给操作系统。这只有在使用间接句柄引用时才有可能,因为否则移动对象将使指向该对象的现有指针无效。间接引用的效率相当低,特别是在现代CPU上,到处追逐指针会对性能造成可怕的影响。

除非程序特别注意,否则通常会发生的情况是,使用brk()分配的每一大块内存都会占用一些仍在使用中的小块内存。

操作系统无法判断程序是否认为该内存仍在使用中,因此它不能只是收回它。由于程序不倾向于访问内存,因此操作系统通常会随着时间的推移将其换出,从而释放物理内存用于其他用途。这是你应该有交换空间的原因之一。

可以编写将内存交还给操作系统的程序,但我不确定您是否可以用Python实现这一点。

另请参阅:

所以:这实际上不是内存泄漏。如果你做的其他事情使用了大量的内存,进程应该不会增长太多,它将重用之前从上次大分配中释放的内存。

票数 11
EN

Stack Overflow用户

发布于 2015-04-16 08:59:44

Joeblog有正确的答案。处理抓取的方式很重要,但比必须定义游标的方式明显得多。这里有一个简单的例子来说明这一点,并为您提供了一些复制-粘贴的开始。

代码语言:javascript
运行
复制
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()

正如您将看到的,点以组的形式快速到达,然后暂停以获得行的缓冲区(迭代大小),因此您不需要使用fetchmany来提高性能。当我使用/usr/bin/time -v运行它时,我在不到3分钟的时间内就得到了结果,1000万行只使用了200MB的内存(而不是客户端游标的60 10 )。服务器不需要更多的内存,因为它使用临时表。

票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17199113

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档