Я использую Postgres 12 с autocommit = off
и пытаюсь избежать взаимных блокировок, используя блокировку экспликации. Когда я выполняю:
ROLLBACK;
SELECT * FROM account WHERE id = 12345 FOR UPDATE;
, где id
является первичным ключом account
. Иногда я захожу в тупик на втором утверждении. Я ожидал бы, что выполнение будет ждать, пока все другие блокировки на линии не будут сняты.
Журнал сервера обычно показывает мне несколько (> 1) конфликтующих транзакций для этого. Но все транзакции, работающие с пользователем, также должны блокировать строку, как указано выше.
Как может возникнуть такая тупик, как указано выше, и как я могу избежать этих тупиков?
Редактировать: Удивительно, но блокировки других процессов, которые показывает мне журнал сервера, также находятся в совершенно разных таблицах, например:
HINT: See server log for query details.
CONTEXT: while locking tuple (2892,8) in relation "account"
LOCATION: DeadLockReport, deadlock.c:1146
STATEMENT: SELECT * FROM account WHERE id = 197375 FOR UPDATE
LOG: 00000: process 17583 detected deadlock while waiting for ShareLock on transaction 1091990904 after 1000.057 ms
DETAIL: Process holding the lock: 17438. Wait queue: .
CONTEXT: while updating tuple (4588,22) in relation "subscription"
Редактировать 2: Я нашел второй тупик в журналах что интересно:
Конфликтный процесс A:
SQL statement "SELECT 1 FROM ONLY "public"."account" x WHERE "id" OPERATOR(pg_catalog.=) $1 FOR KEY SHARE OF x"
SQL statement "WITH inserted_rows AS (
INSERT INTO payment_token (
account_id, ..., blocking_time
)
VALUES (
account_id, ..., the_blocking_time
)
RETURNING *
)
SELECT * FROM inserted_rows"
Первое утверждение не исходит из моего кода напрямую, а второе является частью хранимой функции.
Конфликт процесс B:
UPDATE account SET address_id = $2, update_time = CURRENT_TIMESTAMP WHERE id = $1