Когда и как Postgres использует блокировки "transactionid" - PullRequest
1 голос
/ 17 июня 2020

Я столкнулся с необходимостью выяснить это в версии 9.6, но я был бы признателен за информацию обо всех версиях версии 9.6 или более поздней. он пытается получить неявную блокировку transactionid для другой выполняющейся транзакции. Я не понимаю, почему.

Я понимаю, что каждая транзакция при запуске получает ExclusiveLock на свой собственный идентификатор транзакции ( pg_locks ). Это хорошо. На той же странице сказано, что «обычно» только транзакции, которые «изменяют состояние базы данных», получают постоянный идентификатор. Я вижу постоянный идентификатор в таблице блокировок, поэтому предполагаю, что это произошло. Однако описание менее ясное, поскольку на странице не указано, что означает «обычно», и не указано, что считается «изменением состояния базы данных».

Но тогда я не могу найти никакой информации, которая указывает, когда будет оператор пытается получить SharedLock по какой-либо другой транзакции. Единственный оператор из pg_locks :

Когда процесс обнаруживает необходимость специально дождаться завершения другой транзакции, он делает это, пытаясь получить блокировку общего доступа для другой транзакции. ID (виртуальный или постоянный, в зависимости от ситуации)

Что действительно расплывчато. Невозможно запросить блокировку транзакции (по крайней мере, я не вижу их в списке Explicit Locking )

Поэтому я ищу следующие ответы:

  1. Когда Postgres решает получить (совместно использовать) transactionid блокировку другого идентификатора транзакции?
  2. Что именно заставляет Postgres назначать «постоянный идентификатор» для транзакция (это менее важно для меня, чтобы выяснить, как исправить использование моей базы данных, но меня просто смущало отсутствие точной информации об этом где-либо)
  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)

PID 476513 застрял при попытке вставить:

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)

У меня включено полное ведение журнала, поэтому я также могу увидеть, что произошло PID 476509 делать с момента объявления последнего tra действия. Единственный запрос, который, как я могу себе представить, имеет какое-либо отношение к нему, это тот факт, что он был удален из таблицы 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

(простите некоторые запросы, они созданы JPA, и не мной :))

1 Ответ

2 голосов
/ 17 июня 2020

Обычно это означает, что транзакция ожидает блокировки строки, удерживаемой транзакцией, которую она ожидает.

Блокировки строк хранятся не в таблице блокировок общей памяти постоянно, а в самой строке таблицы в системном столбце xmax. Сохраненное значение - это номер транзакции блокирующей транзакции (обычно).

Как только транзакция обнаруживает, кто удерживает блокировку в этой строке, она начинает ждать завершения этой транзакции sh, что будет освободить монопольную блокировку, которую он удерживает на собственном идентификаторе транзакции.

...