linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
  • PostgreSQL изнутри
    Страница и кортеж, MVCC, vacuum, WAL, планировщик и индексы
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
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
    →
  • PostgreSQL изнутри
    Страница и кортеж, MVCC, vacuum, WAL, планировщик и индексы
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it works
  • Уроки
  • База знаний
  • Собеседование
Cluster

← все кластеры

Блокировки строк, отношений, дедлоки

Где MVCC заканчивается и начинаются настоящие блокировки: режимы блокировок отношений и их конфликты, блокировки строк через xmax, FOR UPDATE и SKIP LOCKED, дедлоки, idle in transaction, очередь блокировок при DDL. Здесь проверяют, понимаешь ли, почему «безобидный» ALTER TABLE кладёт прод.

7 вопросов · ~30 мин чтения

Questions

На этой странице

  1. 01Какие бывают режимы блокировок отношений и какие из них конфликтуют?
  2. 02Как реализованы блокировки строк? Где они хранятся?
  3. 03FOR UPDATE, FOR SHARE, SKIP LOCKED, NOWAIT - когда что применять?
  4. 04Как возникает дедлок и что с ним делает PostgreSQL?
  5. 05Чем опасна транзакция в состоянии idle in transaction?
  6. 06Почему безобидный ALTER TABLE может положить нагруженную базу?
  7. 07Чем lightweight locks и предикатные блокировки отличаются от обычных?

#relation-lock-modes

intermediateчасто

Какие бывают режимы блокировок отношений и какие из них конфликтуют?

Что отвечать

У таблицы есть восемь режимов блокировки от ACCESS SHARE до ACCESS EXCLUSIVE, и они выстроены по силе. Обычный `SELECT` берёт самый слабый ACCESS SHARE; `INSERT`/`UPDATE`/`DELETE` - ROW EXCLUSIVE; команды вроде `CREATE INDEX` - SHARE; большинство `ALTER TABLE`, `DROP`, `TRUNCATE`, `VACUUM FULL` - самый сильный ACCESS EXCLUSIVE, который конфликтует со всем, включая обычный SELECT. Конфликтуют не сами команды, а режимы: чтения совместимы между собой, а ACCESS EXCLUSIVE несовместим ни с чем. Несовместимый запрос встаёт в очередь.

Что хотят услышать

Senior должен: - понимать, что блокировки навешиваются режимами, а конфликт определяет таблица совместимости, а не имя команды - знать ключевые пары: SELECT берёт ACCESS SHARE, DML берёт ROW EXCLUSIVE, тяжёлый DDL берёт ACCESS EXCLUSIVE - объяснить, что ACCESS EXCLUSIVE блокирует даже читателей, и в этом опасность DDL на горячей таблице - уметь смотреть текущие блокировки и ожидания через `pg_locks` и `pg_stat_activity`

Подводные камни

  • ✗ Думать, что `SELECT` ничего не блокирует - он держит ACCESS SHARE, и этого хватает, чтобы заблокировать ACCESS EXCLUSIVE
  • ✗ Считать, что блокировки отношений про строки - это блокировки всей таблицы как объекта
  • ✗ Путать силу режима с именем команды - конфликт решает таблица совместимости режимов

Follow-up

  • ? Почему долгий `SELECT` способен задержать `ALTER TABLE`?
  • ? Какой режим берёт обычный `UPDATE`?
  • ? Как по `pg_locks` понять, кто кого ждёт?

Глубина в базе знаний

  • Тяжёлые блокировки отношений
  • Взаимоблокировки (deadlock)
tags: locks, relation, ddlbook: postgresql_internals-17.pdf:ch13 relation-level locks

#row-locks-implementation

seniorчасто

Как реализованы блокировки строк? Где они хранятся?

Что отвечать

Блокировки строк не лежат в памяти - это было бы слишком дорого при миллионах заблокированных строк. Признак блокировки пишется в саму версию строки: `xmax` получает номер блокирующей транзакции, а биты в `t_infomask` говорят, какая это блокировка - на удаление/изменение или «мягкая» (FOR SHARE/FOR KEY SHARE). Если строку держат несколько транзакций сразу, в `xmax` ставится MultiXact. В памяти живёт лишь короткоживущая блокировка на сам момент изменения версии. Поэтому число заблокированных строк ничем не ограничено - они «бесплатны» по памяти.

Что хотят услышать

Senior должен: - сказать главное: блокировка строки хранится в строке (xmax плюс infomask), а не в общей таблице блокировок в памяти - объяснить, почему так: иначе блокировка миллиона строк съела бы память - связать несколько блокировщиков одной строки с MultiXact - различить блокировку строки и тяжёлую блокировку отношения - это разные механизмы

Подводные камни

  • ✗ Думать, что блокировки строк лежат в `pg_locks` поштучно - там их нет, признак в самой строке
  • ✗ Считать, что много заблокированных строк это нагрузка на память - по памяти они почти бесплатны
  • ✗ Забыть про MultiXact, когда строку держат несколько транзакций

Follow-up

  • ? Почему блокировку миллиона строк не видно поштучно в `pg_locks`?
  • ? Что происходит в `xmax`, когда строку блокируют две транзакции сразу?
  • ? Чем FOR SHARE отличается от FOR UPDATE на уровне битов строки?

Глубина в базе знаний

  • Блокировки строк
  • xmin, xmax и правила видимости
  • Multixact и relminmxid
tags: locks, row, multixactbook: postgresql_internals-17.pdf:ch14 row-level locks

#select-for-update-variants

intermediateчасто

FOR UPDATE, FOR SHARE, SKIP LOCKED, NOWAIT - когда что применять?

Что отвечать

`SELECT ... FOR UPDATE` блокирует выбранные строки на изменение: другие такие же запросы ждут. `FOR SHARE` мягче - разрешает другим тоже читать с блокировкой, но не даёт менять. `FOR KEY SHARE`/`FOR NO KEY UPDATE` - более тонкие варианты, которые меньше конфликтуют между собой и позволяют части UPDATE идти параллельно. Модификаторы поведения при занятой строке: `NOWAIT` сразу падает с ошибкой, а `SKIP LOCKED` пропускает занятые строки и берёт следующие свободные. Связка `FOR UPDATE SKIP LOCKED` - канонический способ построить очередь задач без гонок.

Что хотят услышать

Senior должен: - различить силу: FOR UPDATE против FOR SHARE и более тонкие KEY-варианты - объяснить `SKIP LOCKED` как основу очередей: воркеры разбирают разные строки, не толкаясь - знать `NOWAIT` для случая «лучше сразу ошибка, чем ожидание» - предупредить, что `FOR UPDATE` в долгой транзакции держит строки и горизонт, и это бьёт по очистке

Подводные камни

  • ✗ Строить очередь задач без `SKIP LOCKED` - воркеры выстроятся в очередь на одну строку
  • ✗ Держать `FOR UPDATE` в долгой транзакции - заблокированные строки и горизонт стоят всё это время
  • ✗ Путать `SKIP LOCKED` (пропустить занятые) с `NOWAIT` (упасть с ошибкой)

Follow-up

  • ? Как реализовать очередь задач для нескольких воркеров без гонок?
  • ? Чем `NOWAIT` отличается от `SKIP LOCKED` по поведению?
  • ? Почему долгий `FOR UPDATE` вредит очистке?

Глубина в базе знаний

  • Блокировки строк
  • Тяжёлые блокировки отношений
tags: locks, for-update, queuebook: postgresql_internals-17.pdf:ch14 row-level locks

#deadlocks

intermediateчасто

Как возникает дедлок и что с ним делает PostgreSQL?

Что отвечать

Дедлок - это цикл ожидания: транзакция A держит ресурс 1 и ждёт ресурс 2, а транзакция B держит ресурс 2 и ждёт ресурс 1. Сами по себе они не разойдутся. PostgreSQL не ждёт вечно: через `deadlock_timeout` (по умолчанию секунда) ожидающая транзакция запускает проверку графа ожиданий, находит цикл и откатывает одну из транзакций с ошибкой `deadlock detected`, разрывая цикл. Классическая причина - две транзакции обновляют одни и те же строки в разном порядке. Лечится согласованным порядком блокировок и короткими транзакциями, а не увеличением таймаута.

Что хотят услышать

Senior должен: - определить дедлок как цикл во графе ожиданий и привести пример с обратным порядком обновлений - знать механику: `deadlock_timeout`, построение графа, откат жертвы с ошибкой - дать профилактику: единый порядок захвата строк, короткие транзакции, ретрай на стороне приложения - отличать дедлок (циклическое ожидание, разрывается автоматически) от обычной длительной блокировки (просто ждёт)

Подводные камни

  • ✗ Лечить дедлоки увеличением `deadlock_timeout` - это лишь отодвигает обнаружение, а не убирает причину
  • ✗ Не делать ретрай: дедлок это нормальная ошибка, приложение должно повторить транзакцию
  • ✗ Путать дедлок с долгой блокировкой - вторая разрешится сама, первая нет

Follow-up

  • ? Приведи пример двух транзакций, гарантированно входящих в дедлок
  • ? Что делает `deadlock_timeout` и почему он не равен нулю?
  • ? Как порядок обновления строк помогает избежать дедлоков?

Глубина в базе знаний

  • Взаимоблокировки (deadlock)
  • Блокировки строк
  • Тяжёлые блокировки отношений
tags: locks, deadlock, retrybook: postgresql_internals-17.pdf:ch14 row-level locks

#idle-in-transaction

intermediateчасто

Чем опасна транзакция в состоянии idle in transaction?

Что отвечать

Idle in transaction - открытый `BEGIN`, который ничего не делает, но и не завершён: приложение взяло соединение, выполнило запрос и забыло сделать commit или rollback. Пока транзакция жива, она держит свой снимок, а значит и горизонт: vacuum не может убрать мёртвые версии новее этого снимка, таблицы и индексы раздуваются. Если транзакция к тому же успела что-то заблокировать, она держит и блокировки - очередь за ней растёт. Защита - `idle_in_transaction_session_timeout`, который сам обрывает такие сессии, плюс дисциплина в коде: не оставлять открытых транзакций.

Что хотят услышать

Senior должен: - объяснить двойной вред: удержание горизонта (раздувание) и возможное удержание блокировок (очередь) - связать причину с приложением: пул соединений, забытый commit, долгая работа между запросами внутри транзакции - назвать защиту: `idle_in_transaction_session_timeout` и аккуратные границы транзакций в коде - уметь найти такие сессии в `pg_stat_activity` по состоянию и времени начала транзакции

Подводные камни

  • ✗ Считать idle in transaction безобидным - он держит горизонт и копит мусор не хуже долгого запроса
  • ✗ Открывать транзакцию и ходить в внешние сервисы внутри неё - снимок висит всё это время
  • ✗ Не ставить `idle_in_transaction_session_timeout` и надеяться на дисциплину каждого клиента

Follow-up

  • ? Как найти idle in transaction сессии и их возраст?
  • ? Почему такая сессия мешает vacuum, даже если ничего не блокирует?
  • ? Что делает `idle_in_transaction_session_timeout`?

Глубина в базе знаний

  • idle-in-transaction и каскад блокировок
  • Горизонт транзакции
  • Тяжёлые блокировки отношений
tags: locks, idle-in-transaction, horizonbook: postgresql_internals-17.pdf:ch6 vacuum · codelibs.ru_postgresql-mistakes-and-how-to-avoid-them.pdf:long transactions

#ddl-lock-queue

seniorчасто

Почему безобидный ALTER TABLE может положить нагруженную базу?

Что отвечать

Тяжёлый DDL берёт ACCESS EXCLUSIVE, несовместимый со всем. Если на таблице есть долгий запрос, ALTER встаёт за ним в очередь и сам пока не выполняется. Беда в том, что вставший ALTER блокирует уже всех, кто пришёл после него: даже быстрые SELECT выстраиваются за ним. Один долгий запрос плюс один ALTER превращаются в полную остановку таблицы. Защита - `lock_timeout` перед DDL (лучше упасть, чем заморозить очередь), миграции малыми шагами и приёмы, снижающие силу блокировки: `CREATE INDEX CONCURRENTLY`, `ADD COLUMN` без переписывания таблицы, добавление ограничений через `NOT VALID` с последующим `VALIDATE`.

Что хотят услышать

Senior должен: - объяснить эффект очереди: ожидающий ACCESS EXCLUSIVE блокирует всех пришедших после него - назвать защиту: `lock_timeout` перед миграцией, окна, малые шаги - знать «облегчённые» формы DDL: CONCURRENTLY, NOT VALID плюс VALIDATE, дешёвый ADD COLUMN с DEFAULT начиная с PG 11 - понимать, что причина часто не в самом ALTER, а в долгом запросе или idle in transaction перед ним

Подводные камни

  • ✗ Гнать `ALTER TABLE` на горячей таблице без `lock_timeout` - один долгий запрос превратит это в полную остановку
  • ✗ Строить индекс обычным `CREATE INDEX` на проде вместо `CONCURRENTLY`
  • ✗ Добавлять проверяемое ограничение одним шагом вместо `NOT VALID` плюс `VALIDATE`

Follow-up

  • ? Почему вставший в очередь `ALTER` блокирует даже быстрые SELECT?
  • ? Зачем ставить `lock_timeout` перед миграцией?
  • ? Как добавить проверяемое ограничение, не блокируя таблицу надолго?

Глубина в базе знаний

  • Тяжёлые блокировки отношений
  • idle-in-transaction и каскад блокировок
  • Взаимоблокировки (deadlock)
tags: locks, ddl, migrationbook: postgresql_internals-17.pdf:ch13 relation-level locks · codelibs.ru_postgresql-mistakes-and-how-to-avoid-them.pdf:migrations

#lwlocks-predicate-locks

seniorредко

Чем lightweight locks и предикатные блокировки отличаются от обычных?

Что отвечать

Тяжёлые блокировки (heavyweight) защищают объекты вроде таблиц и строк, видны в `pg_locks`, умеют ждать в очереди и участвуют в обнаружении дедлоков. Lightweight locks (LWLock) защищают внутренние структуры в разделяемой памяти - буферы, списки, кеши; они короткие, бывают в режиме shared/exclusive и не ищут дедлоков. Ещё ниже - спин-блокировки на несколько инструкций. Предикатные блокировки (SIRead) - особый зверь уровня Serializable: они никого не блокируют, а лишь помечают прочитанное, чтобы SSI потом нашёл опасные зависимости. В `pg_locks` они видны как строки с режимом `SIReadLock`, но, в отличие от обычных блокировок, никого не задерживают.

Что хотят услышать

Senior должен: - разложить иерархию: heavyweight (объекты, очередь, дедлоки) - LWLock (структуры памяти) - спинлоки (совсем короткие) - сказать, что LWLock не участвуют в детекте дедлоков и обычно наблюдаются через wait events - объяснить предикатные блокировки как пометки чтения для SSI: они видны в `pg_locks`, но никого не блокируют - связать LWLock-контеншн с диагностикой: высокая доля ожиданий на внутренних блокировках это сигнал узкого места

Подводные камни

  • ✗ Искать LWLock-контеншн в `pg_locks` - его видно через wait events, а не там
  • ✗ Считать предикатные блокировки обычными - они ничего не ждут, только помечают прочитанное
  • ✗ Путать спин-блокировку с тяжёлой - спинлок крутится несколько инструкций и не ждёт в очереди

Follow-up

  • ? Где наблюдать контеншн на lightweight locks?
  • ? Почему предикатные блокировки не вызывают ожиданий?
  • ? Чем спин-блокировка отличается от LWLock по длительности удержания?

Глубина в базе знаний

  • Лёгкие, spin- и предикатные блокировки
  • Тяжёлые блокировки отношений
  • Serializable и SSI
tags: locks, lwlock, predicatebook: postgresql_internals-17.pdf:ch15 miscellaneous locks
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.