Тяжёлая блокировка отношения упорядочивает доступ к таблице как к
объекту: пока кто-то её читает, нельзя посреди этого выполнить
DROP TABLE или переписать формат через ALTER TABLE. Режим
блокировки выбирает не пользователь, а сам PostgreSQL - минимально
достаточный под команду.
Восемь режимов
| Режим | Кто берёт |
|---|---|
| ACCESS SHARE | SELECT |
| ROW SHARE | SELECT ... FOR UPDATE/SHARE |
| ROW EXCLUSIVE | INSERT, UPDATE, DELETE |
| SHARE UPDATE EXCLUSIVE | VACUUM, ANALYZE, CREATE INDEX CONCURRENTLY |
| SHARE | CREATE INDEX |
| SHARE ROW EXCLUSIVE | CREATE TRIGGER, часть ALTER TABLE |
| EXCLUSIVE | REFRESH MATERIALIZED VIEW CONCURRENTLY |
| ACCESS EXCLUSIVE | DROP, TRUNCATE, VACUUM FULL, большинство ALTER TABLE |
Правило конфликтов
Вместо матрицы 8×8 запомни: всё без слова EXCLUSIVE между собой совместимо. SELECT (ACCESS SHARE) и UPDATE (ROW EXCLUSIVE) уживаются. Режимы с EXCLUSIVE конфликтуют с записью и друг с другом, а ACCESS EXCLUSIVE - вообще со всеми, включая обычный SELECT.
До конца транзакции
Тяжёлая блокировка снимается на COMMIT/ROLLBACK, а не на конце
запроса. Поэтому открытый BEGIN с давно отработавшим SELECT всё
ещё держит ACCESS SHARE - и это корень каскадов (см.
idle-in-transaction-cascade).
Где смотреть
SELECT locktype, relation::regclass, mode, granted, pid
FROM pg_locks WHERE relation = 'flights'::regclass;
Строка с granted = f значит, что кто-то ждёт в очереди. Очередь
честная (FIFO), поэтому ждущий с тяжёлым режимом останавливает за
собой даже совместимые запросы. Блокировки строк сюда не попадают -
они в row-locks.