问题描述
谁能向我解释一下以下情况:
我在我的项目中使用 Postgresql 12 作为主要的 rdbms,有几个后台作业并行访问和写入数据库,还有一些用户交互(当然这会从前端产生更新和插入数据库)申请)
我不时会遇到这样的异常:
value
在我的应用程序中,我不会在事务期间手动锁定任何行或表。但是我有“大”事务,可以经常在单个操作中修改很多行。所以问题是:
-
普通事务是产生全表锁还是全行锁? (我假设是的,除非整个情况都很神奇)
-
如果第二个问题的答案是“否”,那么我应该如何处理这种情况?
解决方法
re 1) DML 语句只锁定被修改的行。在 Postgres 中没有锁升级,整个表被锁定以进行写入。有一个“表锁”,但这只是为了防止并发 DDL - SELECT 也会获取它。这些共享锁不会阻止表上的 DML。
re 2) 不,DBMS 无法解决这个问题,因为死锁意味着 tx1 正在等待从 tx2 释放锁,而 tx2 正在等待 tx1 释放锁。 DBMS 如何知道该做什么? DBMS 解决此问题的唯一方法是选择两个会话之一作为受害者并终止事务(这是您看到的错误)。
re 3) 避免死锁的常用方法是始终以相同的顺序更新行。这通常会将死锁变成第二个事务的简单“锁定等待”。
假设以下更新序列
tx1 tx2
-------------------------------
update id = 1 |
| update id = 2
update id = 2 |
(tx1 waits) |
| update id = 1
(now we have a deadlock)
如果你总是更新行,例如升序更改为:
tx1 tx2
-------------------------------
update id = 1 |
| update id = 1
| (waits)
update id = 2 |
|
|
commit; |
(locks released) |
|
| update id = 2
| commit;
因此您不会陷入死锁,只需等待第二个事务。
,-
所有影响表的 SQL 语句都会锁定表(尽管不一定是强锁)。但这似乎不是你的问题。
所有修改行(或
SELECT ... FOR UPDATE
)的 SQL 语句都会锁定受影响的行。您的两个事务可能被行级锁阻塞。 -
是的;这就是错误消息显示的内容。 PostgreSQL 通过杀死其中一个涉及的事务解决了死锁。
如果事务 1 持有事务 2 正在等待的锁,反之亦然,则没有其他方法可以解决这种情况。释放锁的唯一方法是结束持有它的事务。
-
您应该捕获应用程序代码上的错误并重试数据库事务。死锁是一种暂时性错误。
如果您遇到很多死锁,您应该尝试减少它们。什么有助于保持您的交易短小。如果这不是一个选项,请确保所有锁定多行的事务以相同的顺序锁定它们。