> Для чего нужен FOR UPDATE в PostgreSQL (Go, PostgreSQL)
Уровень: senior · Роль: backend · Категория: Технические вопросы
Компании: Ozon, Lamoda
Стек: Go, PostgreSQL
> Пример ответа
Короткий ответ
FOR UPDATE в PostgreSQL - это блокировка строк на уровне SELECT, которая предотвращает их изменение или удаление другими транзакциями до завершения текущей. Она используется для реализации pessimistic locking и защиты от гонок при чтении-модификации-записи. Блокировка снимается при COMMIT или ROLLBACK. Важно: она работает только в рамках транзакции и не блокирует обычные SELECT, только UPDATE, DELETE и другие SELECT ... FOR UPDATE.
Подробное объяснение
FOR UPDATE - это одна из row-level lock-опций в PostgreSQL, применяемая к строкам, возвращаемым SELECT. Когда транзакция выполняет SELECT ... FOR UPDATE, она захватывает exclusive lock на каждую выбранную строку. Это означает:
- Другие транзакции не могут выполнить
UPDATE,DELETEилиSELECT ... FOR UPDATEна этих строках - они будут ждать, пока текущая транзакция завершится. - Обычные
SELECT(без FOR UPDATE) продолжают работать, так как они используют MVCC и не блокируются. - Блокировка снимается только при COMMIT или ROLLBACK транзакции.
Основное назначение - решение проблемы lost update и гонок при паттерне read-modify-write. Например, если два запроса одновременно читают счёт, изменяют его и записывают, без блокировки одно изменение потеряется. FOR UPDATE гарантирует, что только одна транзакция может работать с конкретной строкой одновременно.
Важные нюансы:
FOR UPDATEработает только внутри транзакции (BEGIN/COMMIT).- Можно комбинировать с
SKIP LOCKED- пропустить заблокированные строки, полезно для очередей задач. NOWAIT- немедленно вернуть ошибку, если строка заблокирована, вместо ожидания.- Блокировка применяется к строкам, которые реально возвращает запрос, включая строки из JOIN (если не указано
OF table). - В PostgreSQL 9.5+ есть
FOR UPDATE OF tableдля ограничения блокировки конкретной таблицей. - При использовании индексов блокировка всё равно применяется к строкам таблицы, не к индексам.
На практике
В Go с драйвером pgx или database/sql FOR UPDATE используется в связке с транзакцией. Типичный сценарий - обработка заказов, балансов, инвентаря, где нужно атомарно прочитать и обновить состояние.
Важно помнить про deadlocks: если две транзакции блокируют строки в разном порядке, они могут взаимно ждать друг друга. PostgreSQL обнаруживает deadlock и откатывает одну из транзакций с ошибкой SQLSTATE 40P01.
Также стоит учитывать, что FOR UPDATE увеличивает время удержания блокировок и может снизить конкурентность. Для высоконагруженных систем часто предпочитают optimistic locking (версионирование) или SKIP LOCKED для очередей.
Пример кода
GO// Использование pgxtx, err := db.Begin(ctx)if err != nil {return err}defer tx.Rollback(ctx)var balance interr = tx.QueryRow(ctx,"SELECT balance FROM accounts WHERE id = $1 FOR UPDATE",accountID,).Scan(&balance)if err != nil {return err}newBalance := balance - amountif newBalance < 0 {return errors.New("insufficient funds")}_, err = tx.Exec(ctx,"UPDATE accounts SET balance = $1 WHERE id = $2",newBalance, accountID,)if err != nil {return err}return tx.Commit(ctx)
Как отвечать на собеседовании
Начните с краткого определения: FOR UPDATE - это row-level lock для SELECT. Затем объясните, зачем он нужен: защита от гонок при read-modify-write. Упомяните, что работает только в транзакции и снимается при COMMIT/ROLLBACK.
Покажите понимание MVCC: обычные SELECT не блокируются, только пишущие операции. Расскажите про SKIP LOCKED и NOWAIT как модификаторы для специфичных сценариев. Если спросят про производительность - честно скажите, что это pessimistic locking, он снижает конкурентность, и в некоторых случаях лучше optimistic подход.
Хорошо привести пример из практики: баланс, заказ, очередь задач. Покажите, как в Go это выглядит с транзакцией. Если интервьюер спросит про deadlock - объясните, что PostgreSQL сам их детектит и откатывает одну из транзакций.
Что проверяет интервьюер
- Понимание отличий между MVCC и блокировками.
- Знание, когда нужен pessimistic locking, а когда достаточно optimistic.
- Умение работать с транзакциями в реальном коде.
- Понимание trade-off: блокировки vs конкурентность.
- Знание модификаторов
SKIP LOCKED,NOWAIT,OF. - Осознание рисков deadlock и способов их минимизации.
Типичные ошибки
- Говорить, что
FOR UPDATEблокирует чтение - это не так, обычные SELECT не блокируются. - Забывать, что блокировка снимается только при COMMIT/ROLLBACK, а не после выполнения запроса.
- Использовать
FOR UPDATEбез транзакции - в PostgreSQL это ошибка или бессмысленно. - Не упоминать
SKIP LOCKEDдля очередей - это частый сценарий, который ждут от senior. - Путать
FOR UPDATEс table-level lock (например,LOCK TABLE). - Не учитывать, что при JOIN блокируются строки всех таблиц, если не указано
OF. - Думать, что
FOR UPDATEрешает все проблемы конкурентности - на практике нужно проектировать с учётом длительности транзакций.
> Похожие задачи по backend
Каковы преимущества и недостатки индексов в базе данных
Какие состояния могут быть у горутин в Go
Есть ли у вас мониторинг и на каком стеке он работает
Назови примеры метрик для алертов
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью