Какие виды блокировок существуют в PostgreSQL и как их использовать для решения проблем с конкурентным доступом?
Что проверяют
Знаете ли вы, как PostgreSQL разводит конкурентные изменения, и умеете ли применить это через ORM, а не только в сыром SQL. Типичный следующий вопрос: как в Django сделать так, чтобы два воркера не взяли в работу одну и ту же запись.
Как отвечать
Сначала общий принцип: благодаря MVCC читатели и писатели друг друга не ждут, конфликтуют изменения одних и тех же строк и изменения схемы.
- строковые блокировки:
FOR UPDATEберут перед изменением строки,FOR SHAREзапрещает менять строку, но пускает других с такой же блокировкой. Более слабыеFOR NO KEY UPDATEиFOR KEY SHAREберут самUPDATEи проверки внешних ключей; - табличные: восемь режимов.
ACCESS SHAREу обычногоSELECT,ROW EXCLUSIVEуINSERT,UPDATEиDELETE,SHARE UPDATE EXCLUSIVEуVACUUMиCREATE INDEX CONCURRENTLY,ACCESS EXCLUSIVEу большинстваALTER TABLE; - advisory:
pg_advisory_xact_lock(key)для собственных критических секций, не привязанных к строкам.
В коде: в Django select_for_update() внутри transaction.atomic(), с параметрами skip_locked=True или nowait=True, в SQLAlchemy то же через .with_for_update(skip_locked=True). Очередь задач на таблице строится как SELECT ... FOR UPDATE SKIP LOCKED LIMIT 10: каждый воркер берёт свои строки, не дожидаясь чужих. Иногда блокировка не нужна вовсе: атомарный update(balance=F("balance") + delta) не даёт гонки чтения и записи.
Deadlock PostgreSQL находит сам после deadlock_timeout (по умолчанию 1 секунда) и откатывает одну транзакцию с кодом 40P01. Помогают единый порядок захвата строк и повтор транзакции.
Где спотыкаются
Вызывают select_for_update() вне транзакции. Django падает с TransactionManagementError, а в psycopg с автокоммитом блокировка снимается сразу после запроса.
Путают EXCLUSIVE с ACCESS EXCLUSIVE. Первый разрешает чтение и запрещает изменения, второй блокирует всё, включая SELECT.
Катят миграцию с ALTER TABLE в час пик без lock_timeout. Миграция ждёт долгую транзакцию, а за ней выстраиваются все запросы к таблице.