即使光标关闭,Psycopg 请求也会挂起

Posted

技术标签:

【中文标题】即使光标关闭,Psycopg 请求也会挂起【英文标题】:Psycopg request hangs even if cursor is closed 【发布时间】:2013-08-13 21:43:51 【问题描述】:

我们使用一个对象来保持与 PostgreSQL 数据库的连接并创建新的游标来服务请求。我观察到奇怪的行为:即使读取响应并关闭游标,请求仍然挂在数据库中,阻止更新表等。

当连接关闭时,它就消失了。

我了解 ORM 框架,也许最终会使用其中之一,但我只是想了解这里发生了什么。为什么请求仍然存在?

这是python代码:

import psycopg2

def main():

    conn = psycopg2.connect("dbname=tmpdb password=1 host=localhost")

    cur = conn.cursor()
    cur.execute("SELECT 1;")
    items = cur.fetchall()
    cur.close()
    #uncommenting the following line solves the problem
    #conn.close()

    print items
    while True:
        pass
main()

下面是开始代码的方法:

>python test_loop.py
[(1,)]

观察挂起请求的方法如下:

tmpdb=# SELECT datname,usename,pid,client_addr,waiting,query_start,query FROM pg_stat_activity ;
 datname | usename  |  pid  | client_addr | waiting |          query_start          |                                          query                                           
---------+----------+-------+-------------+---------+-------------------------------+------------------------------------------------------------------------------------------
 tmpdb     | savenkov |   530 | ::1         | f       | 2013-08-12 13:56:32.652996+00 |             SELECT 1;
 tmpdb     | savenkov | 88351 |             | f       | 2013-08-12 13:56:35.331442+00 | SELECT datname,usename,pid,client_addr,waiting,query_start,query FROM pg_stat_activity ;
(2 rows)

【问题讨论】:

所以实际上我现在在任何有意义的请求之后使用SELECT 1; 作为解决方法,但真的期待这个问题的适当解决方案。 【参考方案1】:

你认为它为什么会阻塞?

创建表格

create table t (i integer);

现在运行它:

import psycopg2

def main():

    conn = psycopg2.connect("dbname=cpn")
    cur = conn.cursor()

    cur.execute("SELECT i from t;")
    items = cur.fetchall()
    print items

    raw_input('Enter to insert')

    cur.execute("insert into t (i) values (1) returning i;")
    items = cur.fetchall()
    conn.commit()
    cur.execute("SELECT i from t;")
    items = cur.fetchall()
    print items

    raw_input('Enter to update')

    cur.execute("update t set i = 2 returning i")
    items = cur.fetchall()
    conn.commit()
    cur.execute("SELECT i from t;")
    items = cur.fetchall()
    print items

    cur.close()

    while True:
        pass
main()

请注意,您需要 connection.commit() 才能提交。

话虽如此,但不要进行连接管理。相反,请使用 Pgbouncer 之类的连接池。它将使您免于许多复杂性和挫败感。

如果应用程序与数据库在同一台机器上运行,那么就不用费心了。只要总是根据需要经常关闭连接。如果两者都在一个快速的 Intranet 中,那么增加连接池的复杂性也不值得,除非有非常大量的查询。

【讨论】:

@clodaldo-neto,您可以使用我在上面发布的命令 (SELECT datname,usename,pid,client_addr,waiting,query_start,query FROM pg_stat_activity;) 检查请求表中剩余的内容。我运行你的脚本,最后一个选择挂在请求表中。尝试运行你的脚本,在它获取所有内容并在无限循环中循环之后,制作'drop table t;' - 它不会工作。至于每次创建和删除连接 - 这有效,但不适用于 API 服务大量请求。 @savenkov 它没有挂起。您使连接保持打开状态,并且它处于空闲状态。执行select * from pg_stat_activity 并检查每一列的含义:postgresql.org/docs/9.2/static/…。要使drop 工作,您必须发出connection.comit() @clodaldo-neto,谢谢,是的,它很空闲,我对“挂起”的看法是错误的。酷,我会在每次选择后尝试提交,让我们看看它是否解决了问题。是否有关于提交如何与关闭空闲请求相关的文档? @savenkov 你还是没明白。选择不需要提交,只需要修改数据库的命令,例如更新、插入、删除和 ddl 命令。您不会关闭空闲请求,因为没有空闲请求。有空闲连接是空闲的,因为您没有关闭它们。如果这一切对你来说很奇怪,那么不要,我是认真的,不要管理连接,因为它会伤害你。始终关闭它们并在必要时使用 pgpool if @clodaldo-neto,据我所知,空闲连接中的最后一个请求有效地阻止了表的丢弃。并且在执行了其他一些请求后,可以再次删除该表。我不明白您的评论如何解释这种行为。

以上是关于即使光标关闭,Psycopg 请求也会挂起的主要内容,如果未能解决你的问题,请参考以下文章

Psycopg2 db连接在丢失的网络连接上挂起

即使所有退出事件成功触发,Electron 应用程序也会挂起

如果不使用,数据库会挂起

简单的 Http 请求在 Visual Studio 中执行时挂起,在 powershell 中工作 [关闭]

即使关闭防火墙后,传入请求也会被阻止

Python套接字,从服务器请求文件然后等待接收它