为啥 INSERT 查询无限期挂起并锁定 PostgreSQL 数据库和 pgAdmin
Posted
技术标签:
【中文标题】为啥 INSERT 查询无限期挂起并锁定 PostgreSQL 数据库和 pgAdmin【英文标题】:Why is an INSERT query hanging indefinitely and locking up PostgreSQL database and pgAdmin为什么 INSERT 查询无限期挂起并锁定 PostgreSQL 数据库和 pgAdmin 【发布时间】:2019-12-03 14:44:09 【问题描述】:我有一个 Spring Batch 应用程序(尽管我认为我已经消除了 Spring 作为罪魁祸首 -- 更多内容见下文),它使用 org.postgresql
驱动程序的 42.2.8
版本连接到 PostgreSQL 11.1 数据库,作为AWS RDS 实例。该应用程序正在从 Oracle 设置迁移过来,但是每当我尝试运行应用程序来摄取数据时,它都会无限期地锁定进程,并且在某种程度上似乎锁定了数据库本身......
由于某种原因,当转换为以 postgres 为目标时,可以正常摄取到 Oracle 中的代码似乎完全阻塞了 INSERT 语句(无论是 Spring 生成它们,还是我尝试绕过对 Spring 的所有调用,将INSERT 语句和连接信息硬编码到应用程序主函数的顶部)。
除此之外,在我终止挂起的进程之前,我什至无法在 pgAdmin 中执行某些操作。 Web 界面也无限期挂起,我正试图找出它的韵律或原因(我不能怪表,因为其他表可能会被阻塞,有时可以访问;有时我可以建立新的连接,有时我不能;我似乎确实能够可靠地刷新我以前查询过的表,只要我不杀死选项卡/连接)
我不确定 Spring 是在简单调用周围自动装配一些东西还是自动注入一些东西,所以我将制作一个简单的 Java 应用程序来尝试消除这种可能性......我也可以尝试不同的postgres 驱动程序,但我担心它似乎挂起 pgAdmin Web 界面......但无论如何我希望有人遇到过并修复或缓解这种情况,但它并没有以易于 Google 的方式结束文章还没有
有人有这个问题吗?有没有 Spring Batch?
【问题讨论】:
当您查看pg_stat_activity
时,该会话的状态是什么。 INSERT 可以“锁定”的唯一情况是,如果有另一个未提交的事务插入了相同的主键值。如果您无法刷新已更改的表,这似乎强烈表明您的应用程序在进行更改时缺少提交
我似乎无法建立任何连接来查看该表或自己进行该查询,但我确实要求其他有活动连接的人查询该表并且状态为“活动”,使用 wait_event = "关系", wait_event_type="锁"
然后其他一些会话(事务)有未提交的更改 - 因为这是一个 INSERT,事务很可能插入了相同的值。
@a_horse_with_no_name 冲突键的并发插入将导致阻塞的 transactionid 锁,而不是阻塞的关系锁。关系锁表明另一个会话正在执行真空满、创建索引等类似操作或显式表锁。
okie - 很确定我想通了 - 不知道你们帮了多少忙,但我确实确定有另一个进程被标记为阻塞,这导致我进入另一个保持打开状态的连接,我猜这是由于未提交的截断而阻塞了有问题的表,显然Oracle不介意,或者自动提交(?)......但不管postgres被阻止了。我重组了代码以关闭该连接,它似乎不再阻止事情了。感谢您的耐心等待!
【参考方案1】:
首先,我在 pgAdmin Dashboard 上找到了 Sessions 选项卡,它指向另一个标记为阻止新会话的会话 - 从 python 脚本调用的截断函数,它完成并返回控制(以及所有数据已经从桌子上消失了) - 所以它仍然阻止任何东西并不明显
我发现 this table/matrix of locking/blocking relationships 确实清楚地表明截断会阻止一切:
无论如何...我意识到 python 脚本试图重用步骤 1 中的数据库连接(通过 psycopg2 从 python 直接调用到 Oracle 中的存储过程,或 PostgreSQL 中的函数),然后保持 db 连接(所有步骤都在单个 with 语句块的范围内),因为它为步骤 2 启动了一个 java 进程(它将写入在步骤 1 中被截断的表)。 python 进程会一直等到 java 进程终止,然后将相同的 db 连接传递给第 3 步中直接从 python 调用的存储过程。
对这种方法的批评是肯定的,Oracle 似乎处理得很好,但 Postgres 不同意,锁定在第 2 步,因为我只能从我的阅读和我能找到的少数记录中假设,截断实际上并没有提交或某事(?)
我的解决方法似乎来自于在第 1 步完成后,在开始 Java 进程之前关闭 python 中的连接。然后我获得了第 3 步的另一个连接,现在看来一切正常
【讨论】:
这是因为 Oracle 中的 TRUNCATE 是自动提交的 DDL,而 Postgres 中的 TRUNCATE 是事务性的并且需要提交 - 如果您忘记提交,则表被锁定,因为 TRUNCATE 请求排他表锁定。 好的,所以如果我想保持连接打开以供重用,我需要将TRUNCATE
放在BEGIN .. COMMIT; END;
块中吗?因为那会引发错误:-/ -- the TRUNCATE docs 似乎表明我应该以某种方式提交“周边事务”:- 但the transaction isolation docs 建议一种引发各种错误的语法......???我是关闭连接的唯一选择吗?还是什么?
取决于您如何配置 JDBC 连接。如果您关闭了自动提交(很有可能),那么在截断之后运行 commit
就足够了(或调用 Connection.commit();
)
因此,导致问题的提交的缺失是截断函数调用,它是通过 python 而不是 JDBC 进行的。我认为自动提交通过该 python 驱动程序/连接处于活动状态,因为当我关闭连接时,块被释放,我可以继续并通过 Java 驱动程序进行插入。 ----我要确定的是如何在更细粒度的级别(在存储过程/函数内部)提交截断,但我没有看到任何地方告诉我如何COMMIT
TRUNCATE
(? )
你不能在 Postgres 的函数中 commit
以上是关于为啥 INSERT 查询无限期挂起并锁定 PostgreSQL 数据库和 pgAdmin的主要内容,如果未能解决你的问题,请参考以下文章
为啥 iOS 应用程序图标在 iPhone 6 plus 上安装时会挂起并创建另一个安装图标?
调试随机挂起并使用 100% 处理器内核的 Python 脚本