Postgres "CREATE TABLE AS (SELECT ...)" 卡住了

8

我正在使用Python和psycopg2 2.8.6访问Postgresql 11.6(也尝试过11.9)。

当我运行查询时

CREATE TABLE tbl AS (SELECT (row_number() over())::integer "id", "col" FROM tbl2)

代码出现了卡住的情况(cursor.execute没有返回),利用pg_terminate_backend杀掉进程可以从服务器端删除该查询,但是代码还未释放。但是,在这种情况下,目标表已经被创建。

没有锁定事务。只测试了内部的SELECT查询并且可正常工作。

我尝试在服务器上分析线索,并在pg_stat_activity中找到以下信息:

  • state属性的事务状态为空闲中
  • wait_event_type属性是客户端
  • wait_event属性是ClientRead

当我从SQL编辑器(pgModeler)运行查询时,相同的问题也会发生,但此时查询被Idle状态困住,目标表却已经被创建。

我不确定问题出在哪里以及如何继续处理。 谢谢!


看起来这里有一个类似的问题,https://dev59.com/vmgu5IYBdhLWcg3wdG2o - Sujitmohanty30
2
那么你的“标准SQL编辑器”也有同样的问题。信不信由你:如果会话处于“事务空闲”状态并且正在“等待从客户端读取”,那就是它正在做的事情。是客户端感到困惑,坐在那里无所事事,而不是继续执行。 - Laurenz Albe
@LaurenzAlbe,这种情况发生在特定类型的查询上(查询本身很长,包含对约500个列的引用),使用不同的DB实例和表格(也是长查询)以及2种不同类型的SQL编辑器。在v11.7上也有复现。仅在CREATE TABLE AS SELECT或SELECT INTO查询中出现,并且日志中没有报告任何特殊情况。有没有办法跟踪连接发生了什么以及为什么Postgres认为连接处于等待状态并且客户端被卡住?谢谢 - Meir Tseitlin
你需要跟踪或调试客户端代码。也许有一个超时时间,如果查询时间太长就会导致不正常行为,但我只是猜测。如果两种情况下都使用了psycopg2,那么问题可能出在那里。我可以保证这与数据库服务器无关。 - Laurenz Albe
SELECT (row_number() over())::integer "id", "col" FROM tbl2 返回什么?;-) 另外,您是预期表的副本还是随时间更新的计算表(视图)? - Martial P
我不明白这个问题与编程问题有什么关系。这个问题也存在于SQL编辑器中,所以也许最好将您的问题移动到另一个Stack网站上? - Carlo Zanocco
3个回答

2

我在这里回答自己的问题,以便于对其他人有所帮助。

通过将Postgres设置中的tcp_keepalives_idle从默认的2小时修改为5分钟来解决了该问题。


0

问题无法重现,您需要进行更多的调查。你必须分享更多关于你的数据库表,你的Python代码和服务器操作系统的细节。

您也可以与我们分享附加到Python的strace,以便我们可以看到查询期间实际发生了什么。


  • wait_event_type = Client: 服务器进程正在等待来自用户应用程序的套接字上的某些活动,服务器期望发生独立于其内部进程的事件。 wait_event 将识别特定的等待点。

  • wait_event = ClientRead: 等待 ClientRead 的会话已完成处理上一个查询,并等待客户端发送下一个请求。这样的会话可能会阻止任何操作的唯一方法是它的状态为 idle in transaction。所有锁都将保持到事务结束,事务完成后不再保持任何锁。

  • Idle in transaction: 活动可以是 idle(即等待客户端命令),idle in transaction(在 BEGIN 块中等待客户端)或命令类型名称,如 SELECT。如果服务器进程当前正在等待由另一个会话持有的锁,则附加等待。

问题可能与以下内容有关:

  • 网络问题
  • 在某个地方存在未提交的事务,已经创建了相同的表名。
  • 事务未提交

您指出这不是提交问题,因为SQL编辑器也会执行相同的操作,但在您的问题中,您指定编辑器成功创建了表。

在pgModeler中,您可以看到空闲,这意味着会话处于空闲状态,而不是查询。

如果会话处于空闲状态,则pg_stat_activity的“查询”列显示该会话中执行的最后一个语句。 因此,这仅意味着所有这些会话都使用ROLLBACK语句正确结束了它们的事务。

如果会话长时间保持在事务中空闲状态,则始终存在应用程序错误,即应用程序未结束事务。

您可以做两件事:

  • 设置idle_in_transaction_session_timeout,以便这些事务在一段时间后被服务器自动回滚。这将防止锁定无限期地保持,但您的应用程序将收到一个错误。

  • 按照下面所示修复应用程序


.commit() 解决方案

我发现重现这个问题的唯一方法是省略 commit 操作。

模块 psycopg2 符合 Python DB API 标准,因此默认情况下关闭自动提交功能。

将此选项设置为 False 后,您需要调用 conn.commit 来提交任何待处理的事务到数据库中。

启用自动提交

您可以按照以下步骤启用 自动提交

import psycopg2

connection = None

try:
    connection = psycopg2.connect("dbname='myDB' user='myUser' host='localhost' password='myPassword'")
    connection.autocommit = True
except:
    print "Connection failed."

if(connection != None):
    cursor = connection.cursor()

    try:
        cursor.execute("""CREATE TABLE tbl AS (SELECT (row_number() over())::integer 'id', 'col' FROM tbl2)""")
    except:
        print("Failed to create table.")

with语句

您还可以使用with语句来自动提交事务:

with connection, connection.cursor() as cursor:  # start a transaction and create a cursor
    cursor.execute("""CREATE TABLE tbl AS (SELECT (row_number() over())::integer 'id', 'col' FROM tbl2)""")

传统方式

如果您不想自动提交事务,您需要在执行execute后手动调用.commit()


我不明白这个答案与所问的问题有什么关系。这个问题在SQL编辑器中仍然存在,无论是否使用事务。(请参见评论) - Meir Tseitlin

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接