# Лёгкие, spin- и предикатные блокировки _Блокировки · PostgreSQL Knowledge Base_ **TL;DR:** Под тяжёлыми блокировками лежат лёгкие LWLock (защита структур памяти) и spinlock (пара полей). Их не видно в pg_locks, только в wait_event. Отдельно - предикатные SIReadLock для Serializable, которые не блокируют. У PostgreSQL три слоя блокировок под разные задачи и время жизни. | Слой | Защищает | Живёт | Видно | |---|---|---|---| | Тяжёлые | таблицы, строки | до конца транзакции | `pg_locks` | | LWLock | структуры в shared memory | время операции | `wait_event` | | Spinlock | пара полей | десятки тактов | нигде | ## LWLock Лёгкая блокировка охраняет конкретную структуру в разделяемой памяти: буфер кеша при чтении (см. [buffer-cache](/courses/postgres/kb/buffer-cache.md)), буфер WAL при вставке, таблицу хешей. Два режима (разделяемый/исключительный), но в отличие от тяжёлых - держится микросекунды, не имеет детектора deadlock (берётся в фиксированном порядке, цикл невозможен) и не попадает в `pg_locks`. Имя говорит о цели: `WALInsert`, `BufferContent`, `LockManager`. ## Spinlock Защищает буквально пару инструкций. Не усыпляет процесс - крутится в busy-wait, пока замок занят. Оправдан только при микроскопическом времени удержания: переключение контекста стоило бы дороже. Нигде не учитывается. ## Wait events Раз LWLock и тяжёлые ожидания не сводятся в одну таблицу, общий язык - `wait_event_type`/`wait_event` в `pg_stat_activity`: ```sql SELECT wait_event_type, wait_event, count(*) FROM pg_stat_activity WHERE state = 'active' GROUP BY 1, 2 ORDER BY count(*) DESC; ``` `Lock` - тяжёлая блокировка, `LWLock` - лёгкая, `IO` - диск, пустой тип - работа на CPU. Узкое место ищут семплированием: снять много раз и посмотреть, что чаще. ## Предикатные блокировки (SIReadLock) Их использует Serializable/SSI (см. [serializable-ssi](/courses/postgres/kb/serializable-ssi.md)), и они не блокируют. При чтении на Serializable транзакция оставляет метку `SIReadLock` - «я это читала». Никто не ждёт. Но если другая транзакция пишет туда, где стоит чужая метка, и шаблон зависимостей опасен, при коммите будет ошибка сериализации `40001`. Огрубляются кортеж → страница → таблица ради памяти, ценой ложных срабатываний. ```sql SELECT count(*) FROM pg_locks WHERE mode = 'SIReadLock'; ``` В отличие от [deadlocks](/courses/postgres/kb/deadlocks.md), конфликт SSI всплывает не циклом, а на коммите. Тяжёлый слой описан в [relation-locks](/courses/postgres/kb/relation-locks.md). ## Команды ```sql SELECT wait_event_type, wait_event FROM pg_stat_activity WHERE state='active'; ``` На чём прямо сейчас ждут активные бэкенды ```sql SELECT mode, count(*) FROM pg_locks WHERE mode='SIReadLock' GROUP BY 1; ``` Следы предикатных блокировок Serializable ```sql BEGIN ISOLATION LEVEL SERIALIZABLE; ``` Включить SSI: чтения начнут оставлять SIReadLock ## См. также - [Взаимоблокировки (deadlock)](/courses/postgres/kb/deadlocks.md) - [Тяжёлые блокировки отношений](/courses/postgres/kb/relation-locks.md) - [Блокировки строк](/courses/postgres/kb/row-locks.md) - [Буферный кеш и вытеснение](/courses/postgres/kb/buffer-cache.md) - [Serializable и SSI](/courses/postgres/kb/serializable-ssi.md)