# Блокировки строк _Блокировки · PostgreSQL Knowledge Base_ **TL;DR:** Блокировка строки хранится в самой строке (xmax + infomask), а не в pg_locks. Режимов четыре: FOR KEY SHARE, FOR SHARE, FOR NO KEY UPDATE, FOR UPDATE. Слабые нужны, чтобы внешние ключи не сериализовали вставки. Когда `UPDATE` или `DELETE` трогает строку, он блокирует именно её. На таблице при этом висит лёгкий ROW EXCLUSIVE, а сам замок на строку живёт в данных: номер держащей транзакции пишется в поле `xmax` заголовка кортежа, статус - в битах `t_infomask`. Поэтому блокировка строки почти бесплатна по памяти и масштабируется на миллионы строк. ## Четыре режима | Режим | Сила | Когда | |---|---|---| | FOR KEY SHARE | слабейший | удержать строку от удаления и смены ключа | | FOR SHARE | разделяемый | «никто не изменит, пока считаю» | | FOR NO KEY UPDATE | средний | `UPDATE` неключевых колонок | | FOR UPDATE | сильнейший | `SELECT ... FOR UPDATE`, `DELETE` | Зачем четыре? Из-за внешних ключей. Вставка в дочернюю таблицу должна удержать родителя от исчезновения, но не менять его. Слабый FOR KEY SHARE блокирует только удаление и смену ключа родителя, пропуская параллельный `UPDATE` его неключевых колонок. До версии 9.3 тут был полный конфликт, и вставки в дочернюю таблицу сериализовались зря. ## Где это видно В `pg_locks` отдельной строки на заблокированный кортеж нет. Ждущая транзакция видна косвенно - она стоит на `locktype = 'transactionid'`, ожидая завершения держателя: ```sql SELECT pid, wait_event_type, wait_event FROM pg_stat_activity WHERE wait_event_type = 'Lock'; ``` Если одну строку держат несколько транзакций в режиме FOR SHARE, одного `xmax` мало - заводится multixact (запись в `pg_multixact` со списком участников). Его возраст отслеживает `relminmxid`, и он тоже требует заморозки. ## Главное про порядок Брать строки в одном порядке во всех транзакциях (например, по возрастанию ключа) - так не возникает [deadlocks](/courses/postgres/kb/deadlocks.md). Сами блокировки строк - это часть тяжёлого механизма, верхний слой которого описан в [relation-locks](/courses/postgres/kb/relation-locks.md). ## Команды ```sql SELECT * FROM bookings WHERE book_ref = '000001' FOR UPDATE; ``` Явно заблокировать строку под последующее изменение ```sql SELECT * FROM bookings WHERE book_ref = '000001' FOR UPDATE SKIP LOCKED; ``` Пропустить уже заблокированные строки - паттерн очереди задач ```sql SELECT * FROM bookings WHERE book_ref = '000001' FOR UPDATE NOWAIT; ``` Не ждать: если строка занята, сразу ошибка ## См. также - [Тяжёлые блокировки отношений](/courses/postgres/kb/relation-locks.md) - [Взаимоблокировки (deadlock)](/courses/postgres/kb/deadlocks.md) - [idle-in-transaction и каскад блокировок](/courses/postgres/kb/idle-in-transaction-cascade.md)