linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it worksскоро
  • Уроки
  • База знаний
  • Собеседование
home/postgres/kb/Блокировки/idle-in-transaction-cascade

kb/locks ── Блокировки ── intermediate

idle-in-transaction и каскад блокировок

Забытый открытый BEGIN держит блокировку до конца транзакции. Если за ним в честной FIFO-очереди встанет ALTER TABLE (ACCESS EXCLUSIVE), то даже совместимые SELECT за ним тоже повиснут - встаёт вся таблица.

view as markdownaka: idle-in-transaction, lock-cascade, lock-queue

Это самый частый способ положить базу одной строкой. Разберём по шагам, как безобидное сочетание превращается в каскад.

Сцена из трёх сессий

sql
-- A: открыл транзакцию, прочитал, забыл закрыть
BEGIN;
SELECT count(*) FROM flights;   -- ACCESS SHARE; state = idle in transaction
-- B: выкатывается миграция
ALTER TABLE flights ADD COLUMN gate text;  -- хочет ACCESS EXCLUSIVE → ждёт A
-- C: обычный пользовательский запрос
SELECT * FROM flights WHERE flight_no = 'PG1';  -- ACCESS SHARE → ждёт B

A и C совместимы - два читателя. Но между ними стоит B с ACCESS EXCLUSIVE, а очередь блокировок честная: новые запросы не перепрыгивают ждущего. Поэтому C виснет за B, а B - за A. Один забытый BEGIN плюс одна миграция останавливают всю таблицу.

В чём именно ловушка

Опасен не ALTER TABLE сам по себе, а его сочетание с долгой открытой транзакцией впереди. Тяжёлая блокировка держится до конца транзакции (см. relation-locks), а не до конца запроса. Сессия в состоянии idle in transaction ничего не делает, но всё ещё держит ACCESS SHARE.

Диагностика

sql
SELECT w.pid AS waiting, b.pid AS blocking, b.state, b.query
FROM pg_stat_activity w
JOIN LATERAL unnest(pg_blocking_pids(w.pid)) AS bp(pid) ON true
JOIN pg_stat_activity b ON b.pid = bp.pid
WHERE cardinality(pg_blocking_pids(w.pid)) > 0;

pg_blocking_pids(pid) сразу называет корень. Если виновник в idle in transaction, снять его pg_cancel_backend чаще всего не выйдет - там нет активного запроса. Нужен pg_terminate_backend: он закроет сессию, транзакция откатится, очередь рассосётся.

Профилактика

  • idle_in_transaction_session_timeout прибивает забытые транзакции;
  • lock_timeout не даёт DDL стоять в очереди бесконечно;
  • короткие транзакции и COMMIT сразу после работы.

В отличие от deadlocks, каскад - это не цикл: его не разрывает детектор, он будет стоять, пока не вмешаешься.

§ команды

bash
SELECT pid, state, query FROM pg_stat_activity WHERE state = 'idle in transaction';

Найти забытые открытые транзакции

bash
SELECT pg_terminate_backend(<pid>);

Закрыть сессию-блокировщик: транзакция откатится, блокировки спадут

bash
ALTER SYSTEM SET idle_in_transaction_session_timeout = '30s';

Автоматически закрывать транзакции, висящие без активности

§ см. также

  • relation-locksТяжёлые блокировки отношенийКаждая команда берёт на таблицу блокировку одного из 8 режимов: от ACCESS SHARE (обычный SELECT) до ACCESS EXCLUSIVE (DROP/ALTER), который конфликтует со всеми. Блокировка держится до конца транзакции.
  • row-locksБлокировки строкБлокировка строки хранится в самой строке (xmax + infomask), а не в pg_locks. Режимов четыре: FOR KEY SHARE, FOR SHARE, FOR NO KEY UPDATE, FOR UPDATE. Слабые нужны, чтобы внешние ключи не сериализовали вставки.
  • deadlocksВзаимоблокировки (deadlock)Deadlock - цикл в графе ожидания: каждая транзакция ждёт ту, что ждёт её. PostgreSQL находит его через deadlock_timeout (1 с) и откатывает одну транзакцию с ошибкой 40P01. Лечится единым порядком блокировок.
Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки