关于 Postgresql 死锁的问题

问题描述

谁能向我解释一下以下情况:

我在我的项目中使用 Postgresql 12 作为主要的 rdbms,有几个后台作业并行访问和写入数据库,还有一些用户交互(当然这会从前端产生更新和插入数据库)申请)

我不时会遇到这样的异常:

value

在我的应用程序中,我不会在事务期间手动锁定任何行或表。但是我有“大”事务,可以经常在单个操作中修改很多行。所以问题是:

  1. 普通事务是产生全表锁还是全行锁? (我假设是的,除非整个情况都很神奇)

  2. 当两个查询试图修改一个资源时,如果它们被包裹在事务中,rdbms 不应该自动解决这类问题吗?

  3. 如果第二个问题的答案是“否”,那么我应该如何处理这种情况?

解决方法

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;

因此您不会陷入死锁,只需等待第二个事务。

,
  1. 所有影响表的 SQL 语句都会锁定表(尽管不一定是强锁)。但这似乎不是你的问题。

    所有修改行(或 SELECT ... FOR UPDATE)的 SQL 语句都会锁定受影响的行。您的两个事务可能被行级锁阻塞。

  2. 是的;这就是错误消息显示的内容。 PostgreSQL 通过杀死其中一个涉及的事务解决了死锁。

    如果事务 1 持有事务 2 正在等待的锁,反之亦然,则没有其他方法可以解决这种情况。释放锁的唯一方法是结束持有它的事务。

  3. 您应该捕获应用程序代码上的错误并重试数据库事务。死锁是一种暂时性错误。

    如果您遇到很多死锁,您应该尝试减少它们。什么有助于保持您的交易短小。如果这不是一个选项,请确保所有锁定多行的事务以相同的顺序锁定它们。