Psycopg / Postgres:连接随机挂起

3 投票
3 回答
3005 浏览
提问于 2025-04-16 07:01

我正在用psycopg2来开发一个cherrypy应用,同时用命令行和phpgadmin来手动处理一些操作。以下是我的Python代码:

#One connection per thread
cherrypy.thread_data.pgconn = psycopg2.connect("...") 
...
#Later, an object is created by a thread :
class dbobj(object):
 def __init__(self):
  self.connection=cherrypy.thread_data.pgconn
  self.curs=self.connection.cursor(cursor_factory=psycopg2.extras.DictCursor)
...
#Then,
try:
 blabla
 self.curs.execute(...)
 self.connection.commit()
except:
 self.connection.rollback()
 lalala
...
#Finally, the destructor is called :
def __del__(self):
 self.curs.close()

我遇到了一个问题,可能是psycopg或者postgres的问题(不过我觉得后者更有可能)。在发送了几条查询后,我的连接就断开了。phpgadmin通常也会掉线;在多次请求后,它会提示我重新连接。只有命令行工具能一直保持连接。

这个问题很随机,我甚至无法找到原因。有时候在请求几次页面后就会断开连接,而有时候在请求了几百个页面后却没问题。我在postgres的日志中找到的唯一错误是在我结束应用后出现的:

...
LOG:  unexpected EOF on client connection
LOG:  could not send data to client: Broken pipe
LOG:  unexpected EOF on client connection
...

我考虑过每次创建新的dbobj实例时都建立一个新连接,但我绝对不想这样做。

另外,我也听说如果不提交所有事务可能会遇到类似的问题:我对每一个INSERT/UPDATE查询都使用了try/except块,但对SELECT查询从来没有用过,也不想写更多的样板代码(顺便问一下,SELECT查询需要提交吗?)。即使是这样,为什么phpgadmin会掉线呢?

在.conf文件中,max_connections设置为100,所以我觉得这也不是原因。一个cherrypy工作进程只有10个线程。

有没有人知道我应该先从哪里入手呢?

3 个回答

0

我看到你在使用 cherrypy.thread_data 的时候,有点难以理解你是怎么填充和访问它的。我建议你可以看看 psycopg2.pool.ThreadedConnectionPool,而不是自己去把一个连接绑定到每个线程上。

2

Psycopg2在每次操作后都需要进行提交(commit)或回滚(rollback),即使是简单的SELECT查询也不例外,否则连接会处于“事务空闲”状态。这在文档中现在已经有了警告:

警告:默认情况下,任何查询执行,包括简单的SELECT,都会启动一个事务;对于长时间运行的程序,如果没有进一步的操作,会导致会话保持在“事务空闲”状态,这种情况是不理想的(因为会话会占用锁,表会膨胀……)。对于长时间运行的脚本,要么确保尽快结束事务,要么使用自动提交的连接。
0

虽然我不知道为什么成功的 SELECT 查询会阻塞连接,但在几乎每个不需要和其他查询一起工作的查询后面加上 .commit(),就解决了这个问题。

撰写回答