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/lessons/pg-lab-21-1-relation-row-locks

lesson ── postgres-labs ── ~25 мин ── 5 шагов

Поймай каскад блокировок от idle-in-transaction

Четыре клиентские сессии на один сервер. Ты соберёшь руками каскад, который в проде выглядит как «всё висит после деплоя»: забытая транзакция, за ней миграция, за ней обычные запросы. Потом найдёшь корень по PID и снимешь его.

Вкладки: psql-a - забытая транзакция, psql-b - миграция, psql-c

  • пользователь, psql-ops - ты-дежурный, который разбирает завал. В каждой вкладке сначала запусти psql.

▶ интерактивный sandbox

Поднимется контейнер postgreslab/postgres-base с PostgreSQL 17 и psql. В браузере откроется терминал, база lab уже настроена. Каждый шаг проверяется автоматически. Сеть air-gapped, наружу контейнер не ходит.

запустить sandbox →

stack ── PostgreSQL 17 · psql · 1 GB RAM · air-gapped · самоуничтожается через 45 мин простоя

Шаги

  1. 01

    Сессия A держит таблицу и забывает закоммитить

    Открой psql во вкладке psql-a и начни транзакцию, не закрывая её:

    sql
    BEGIN;
    SELECT count(*) FROM flights;

    Приглашение стало lab=*# - звёздочка значит «транзакция открыта». Запрос отработал, но ACCESS SHARE на flights держится. Не вводи COMMIT.

    подсказка

    Звёздочка в приглашении psql = открытая транзакция. Это и есть idle in transaction.

    ✓ A висит в idle in transaction и держит ACCESS SHARE на flights.

  2. 02

    Сессия B выкатывает миграцию

    Перейди во вкладку psql-b, запусти psql и выполни:

    sql
    ALTER TABLE flights ADD COLUMN gate text;

    Команда не вернёт приглашение - она повисла. ALTER TABLE хочет ACCESS EXCLUSIVE, который конфликтует с ACCESS SHARE сессии A. Предскажи: сколько невыданных (granted = f) блокировок по flights сейчас в pg_locks?

    подсказка

    Невыданная блокировка - это строка pg_locks с granted = f. Её ждёт B.

    ✓ B встал в очередь за ACCESS EXCLUSIVE - есть невыданная блокировка.

  3. 03

    Сессия C - обычный SELECT - тоже виснет

    Вкладка psql-c, запусти psql, потом:

    sql
    SELECT * FROM flights LIMIT 1;

    C совместима с A (два читателя), но впереди в очереди стоит B с ACCESS EXCLUSIVE, а очередь честная - перепрыгнуть ждущего нельзя. Поэтому C тоже виснет. Предскажи, сколько сессий теперь ждут блокировку (wait_event_type = 'Lock').

    подсказка

    Ждут и B, и C. wait_event_type='Lock' - именно про ожидание тяжёлой блокировки.

    ✓ Две сессии в очереди. Один SELECT остановил всю таблицу.

  4. 04

    Найди корень каскада по PID

    Во вкладке psql-ops запусти psql и найди, кто кого блокирует:

    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;

    Цепочка ведёт к сессии A в состоянии idle in transaction - это корень. Запиши её PID (колонка blocking с state = idle in transaction).

    подсказка

    pg_blocking_pids(pid) возвращает массив тех, кто мешает данному процессу.

    ✓ Видно две заблокированные сессии, корень - idle-in-transaction A.

  5. 05

    Сними блокировщика и проверь, что завал ушёл

    Корень в idle in transaction, значит pg_cancel_backend не поможет (нет активного запроса). Нужен pg_terminate_backend. Во вкладке psql-ops найди PID сессии A и закрой её:

    sql
    -- подставь PID сессии A из прошлого шага
    SELECT pg_terminate_backend(<pid_A>);

    Как только A закрыта, её ACCESS SHARE спадает: B применяет миграцию, C получает результат. Проверим по факту - в flights появилась колонка gate.

    подсказка

    pg_terminate_backend закрывает соединение целиком: транзакция откатывается, блокировки спадают.

    ✓ Миграция прошла, колонка gate на месте. Каскад разобран.

Что ты узнал

Каскад блокировок - это забытая открытая транзакция, за которой в честной FIFO-очереди встал DDL с ACCESS EXCLUSIVE, а за ним - обычные запросы. Тяжёлая блокировка держится до конца транзакции, поэтому idle-in-transaction опасен. Диагностика идёт от ждущего к держащему через pg_blocking_pids, лечение - pg_terminate_backend.

команды

  • SELECT * FROM ... WHERE ... FOR UPDATE;явно заблокировать строки
  • SELECT pid, pg_blocking_pids(pid) FROM pg_stat_activity;кто кого блокирует
  • SELECT pg_terminate_backend(pid);закрыть сессию-блокировщик
  • SELECT mode, granted FROM pg_locks WHERE relation='t'::regclass;блокировки на таблице

концепции

  • · ACCESS EXCLUSIVE конфликтует со всеми режимами, включая обычный SELECT
  • · тяжёлая блокировка держится до конца транзакции, не до конца запроса
  • · очередь блокировок честная - ждущий останавливает за собой совместимые запросы
  • · idle in transaction держит блокировку, снимается через pg_terminate_backend

← предыдущая

Разбери журнал по записям через pg_walinspect

следующая →

Воспроизведи и прочитай взаимоблокировку

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки