Швидкий запит - хто чекає й на кого:
SELECT waiting.pid AS waiting_pid,
waiting.query AS waiting_query,
now() - waiting.query_start AS waiting_for,
blocking.pid AS blocking_pid,
blocking.state AS blocking_state,
blocking.query AS blocking_query
FROM pg_stat_activity waiting
JOIN LATERAL unnest(pg_blocking_pids(waiting.pid)) AS b(pid) ON true
JOIN pg_stat_activity blocking ON blocking.pid = b.pid
WHERE waiting.wait_event_type = 'Lock';
pg_blocking_pids(pid) повертає процеси, що блокують даний, - найпростіший шлях до відповіді.
На що дивитися в результаті:
blocking_state = 'idle in transaction'- класика: застосунок відкрив транзакцію, щось змінив і «забув» її закрити (чекає на HTTP-запит, впав посередині, тримає транзакцію в довгому циклі).- Довгий
blocking_query- звіт, міграція, масове оновлення. - Ланцюжки - A чекає на B, B чекає на C; корінь - той, хто сам ні на кого не чекає.
Як розблокувати в аварійній ситуації:
SELECT pg_cancel_backend(12345); -- скасувати поточний запит процесу
SELECT pg_terminate_backend(12345); -- розірвати з'єднання повністю
pg_cancel_backend м'якший, але не допоможе з idle in transaction - там немає запиту, який можна скасувати, лише terminate.
Детальніше - pg_locks: усі утримувані й очікувані блокування з режимами й об'єктами (relation::regclass). Корисно, щоб зрозуміти, яке саме блокування конфліктує.
Профілактика: log_lock_waits = on (логувати очікування довші за deadlock_timeout), idle_in_transaction_session_timeout, lock_timeout для міграцій, моніторинг кількості очікуючих сесій.