Postgres 何时以及如何使用“transactionid”锁

Paw*_*lov 6 postgresql database-locking postgresql-9.6

我遇到了需要在 9.6 上解决这个问题,但是关于 9.6 或更高版本的任何信息将不胜感激。

我的应用程序在数据库调用时被阻塞,因为它试图获取transactionid另一个正在运行的事务的隐式锁,我遇到了问题。我不明白的是为什么。

我知道每个事务在开始时都会获取一个ExclusiveLock自己的事务 ID ( pg_locks )。没关系。同一页面说,“通常”,只有“更改数据库状态”的事务才会被分配一个永久 ID。我在锁表中看到了永久 ID,所以我认为这发生了。但是,描述不太清楚,因为该页面既没有说明“通常”的含义,也没有说明什么是“数据库状态的变化”。

但是后来我找不到任何信息来指定语句何时会尝试获取SharedLock某个其他交易的 a。pg_locks的唯一声明是:

当一个进程发现有必要专门等待另一个事务结束时,它会尝试获取另一个事务 ID 的共享锁(虚拟或永久 ID,具体取决于情况)

这真的很模糊。没有办法请求事务锁(至少我没有在Explicit Locking 中看到它们)

所以我正在寻找以下答案:

  1. Postgres 何时决定获取(共享)transactionid另一个事务 ID 的锁?
  2. 究竟是什么导致 Postgres 为事务分配“永久 ID”(这对我来说,弄清楚如何修复我的数据库使用不太重要,但我只是因为在任何地方都缺乏关于此的硬信息而感到困惑)
  3. transactionid在这种特殊情况下是什么让插入查询获得锁(也不太重要,因为如果我有参考,我可以自己确定)

现在,出于特殊原因,我的查询在这种情况下被阻止:

相关pg_locks内容:

x=# select locktype,transactionid,virtualtransaction,pid,mode,granted
x-# from pg_locks where transactionid = '33682979' ;
   locktype    | transactionid | virtualtransaction |  pid   |     mode      | granted 
---------------+---------------+--------------------+--------+---------------+---------
 transactionid |      33682979 | 7/27909            | 476513 | ShareLock     | f
 transactionid |      33682979 | 5/387791           | 476509 | ExclusiveLock | t
(2 rows)
Run Code Online (Sandbox Code Playgroud)

PID476513在尝试插入时卡住:

x=# SELECT wait_event_type, state, query 
x-# FROM pg_stat_activity 
x-# WHERE pid = 476513;
 wait_event_type | state  |                              query                                                                                                                     
-----------------+--------+--------------------------------------------------------------------
 Lock            | active | INSERT INTO association (id, device, campaign) VALUES ($1, $2, $3)
(1 row)
Run Code Online (Sandbox Code Playgroud)

我启用了完整的语句日志记录,所以我还可以看到476509自声明最后一个事务以来PID做了什么。我能想象的唯一与它有关的查询是它已从association表中删除。

$ grep '476509.*execute' tx-lock.txt
<2020-06-17 13:58:37.743 CEST 476509.5/387791> LOG:  execute S_13: BEGIN
<2020-06-17 13:58:37.743 CEST 476509.5/387791> LOG:  execute <unnamed>: SELECT t0.* FROM campaign t0 WHERE t0.id = $1 FOR UPDATE
<2020-06-17 13:58:37.744 CEST 476509.5/387791> LOG:  execute <unnamed>: SELECT t0.* FROM campaign t0 WHERE t0.id = $1 FOR UPDATE
<2020-06-17 13:58:37.752 CEST 476509.5/387791> LOG:  execute <unnamed>: SELECT t0.* FROM association t0 WHERE (t0.enabled = $1 AND $2 = t0.campaign AND t0.statusCreated <> $3) LIMIT $4
<2020-06-17 13:58:37.759 CEST 476509.5/387791> LOG:  execute <unnamed>: DELETE FROM association WHERE id IN (SELECT DISTINCT t0.id FROM association t0 WHERE (t0.campaign = $1))
<2020-06-17 13:58:37.796 CEST 476509.5/387791> LOG:  execute <unnamed>: UPDATE campaign SET statusCreated = $1 WHERE id = $2
<2020-06-17 13:58:37.796 CEST 476509.5/387791> LOG:  execute S_42: SELECT t0.id FROM lock t0 WHERE t0.id = $1
<2020-06-17 13:58:37.796 CEST 476509.5/387791> LOG:  execute S_31: select id from lock where id = $1 for update
<2020-06-17 13:58:37.798 CEST 476509.5/387791> LOG:  execute <unnamed>: SELECT t0.*, t1.*id FROM groups t0 INNER JOIN devices t1 ON t0.device_id = t1.id AND t0.device_tenancy = t1.tenancy LEFT OUTER JOIN group_defs t2 ON t0.DEVICEGROUP_ID = t2.id WHERE ((t0.group_id = $1...) AND t1.tenancy = $36) ORDER BY t0.id ASC, t1.id ASC LIMIT $37
Run Code Online (Sandbox Code Playgroud)

(请原谅一些查询,它们是由 JPA 创建的,而不是由我创建的 :))

Lau*_*lbe 6

这通常表示事务等待它正在等待的事务持有的行锁。

行锁不是永久存储在共享内存锁定表中,而是存储在xmax系统列中的表行本身。存储在那里的值是阻塞事务的事务号(通常)。

一旦事务发现谁持有该行的锁,它就会开始等待该事务完成,这将释放它在自己的事务 ID 上持有的排他锁。