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-22-1-deadlocks

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

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

Два «перевода денег» в обратном порядке - и получаем цикл, который не разрешится сам. PostgreSQL подождёт deadlock_timeout (1 секунда), обнаружит цикл и откатит одну из транзакций. Ты соберёшь это руками, поймаешь ошибку 40P01, прочитаешь описание цикла в логе сервера и убедишься по счётчику pg_stat_database.deadlocks, что тупик случился.

Вкладки: psql-a и psql-b - две конкурирующие транзакции, 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-a, запусти psql:

    sql
    BEGIN;
    UPDATE bookings SET total = total - 10 WHERE book_ref = '000001';

    Строка 000001 теперь заблокирована транзакцией A (её номер записан в xmax строки). Транзакция открыта - не коммить.

    подсказка

    UPDATE блокирует затронутую строку до конца транзакции.

    ✓ A держит строку 000001.

  2. 02

    B захватывает вторую строку

    Вкладка psql-b, запусти psql:

    sql
    BEGIN;
    UPDATE bookings SET total = total - 10 WHERE book_ref = '000002';

    Теперь каждая транзакция держит «свою» строку. Цикла пока нет - обе ещё могут завершиться.

    подсказка

    Две открытые транзакции, каждая со своей заблокированной строкой.

    ✓ B держит строку 000002. Сцена готова.

  3. 03

    A тянется за чужой строкой и виснет

    Вернись в psql-a:

    sql
    UPDATE bookings SET total = total + 10 WHERE book_ref = '000002';

    Команда повисла: строку 000002 держит B. A ждёт завершения транзакции B. Предскажи: сколько сессий сейчас в ожидании wait_event_type = 'Lock'?

    подсказка

    A ждёт транзакцию B - это ожидание тяжёлой блокировки, тип Lock.

    ✓ A стоит в ожидании. Осталось замкнуть цикл.

  4. 04

    B замыкает цикл - срабатывает детектор

    Вернись в psql-b и потяни за строку, которую держит A:

    sql
    UPDATE bookings SET total = total + 10 WHERE book_ref = '000001';

    Теперь A ждёт B, а B ждёт A - цикл. Примерно через секунду (deadlock_timeout) одна из транзакций получит ERROR: deadlock detected. Проверим счётчик базы.

    подсказка

    Детектор запускается не сразу, а спустя deadlock_timeout (1 с).

    ✓ deadlocks > 0 - сервер обнаружил и разорвал цикл.

  5. 05

    Прочитай лог и расчисти очередь

    Открой лог сервера (вкладка pg в окне сессии или docker logs ноды pg) и найди блок:

    ERROR:  deadlock detected
    DETAIL:  Process ... waits for ... blocked by process ...
    CONTEXT:  while updating tuple (...) in relation "bookings"

    DETAIL описывает цикл по PID, CONTEXT называет конкретный кортеж. Выжившая транзакция получила свою строку - закоммить её (COMMIT в той вкладке, где не было ошибки). Жертва откатилась целиком. Проверим, что в очереди никто не висит.

    подсказка

    После разрыва цикла ни одна сессия не должна ждать Lock.

    ✓ Очередь чиста. Жертва откатилась, выживший закоммитился.

Что ты узнал

Взаимоблокировка - цикл в графе ожидания, который сервер находит через deadlock_timeout и разрывает, откатывая одну транзакцию с ошибкой 40P01. Лог в DETAIL описывает цикл по PID, в CONTEXT - кортеж. Счётчик pg_stat_database.deadlocks подтверждает событие. Лекарство - единый порядок блокировки строк и ретрай на 40P01.

команды

  • SELECT deadlocks FROM pg_stat_database WHERE datname=current_database();счётчик взаимоблокировок
  • SHOW deadlock_timeout;задержка перед запуском детектора
  • SELECT ... ORDER BY id FOR UPDATE;блокировать в едином порядке
  • SELECT count(*) FROM pg_stat_activity WHERE wait_event_type='Lock';сколько сессий ждут блокировку

концепции

  • · deadlock - цикл в графе ожидания, сам не разрешается
  • · детектор запускается через deadlock_timeout, не мгновенно
  • · жертва получает полный ROLLBACK и ошибку 40P01
  • · единый порядок блокировок делает цикл невозможным

← предыдущая

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

следующая →

Прочитай wait events и предикатные блокировки

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