Advisory Locks в PostgreSQL: скрытые блокировки уровня приложения
Узнайте, как использовать Advisory Locks в PostgreSQL для управления конкурентным доступом без накладных расходов традиционных блокировок.
Что такое Advisory Locks в PostgreSQL
В PostgreSQL Advisory Locks – это механизм пользовательских блокировок, который позволяет приложению синхронизировать доступ к произвольным ресурсам без участия планировщика транзакций. В отличие от обычных row‑level или table‑level блокировок, advisory‑блокировки не привязаны к данным и могут использоваться для контроля доступа к внешним объектам: файлам, очередям, кешам и даже к отдельным бизнес‑процессам.
Технически advisory‑блокировки реализованы через функции pg_advisory_lock, pg_advisory_xact_lock и их аналоги для разблокировки. Они работают на уровне PostgreSQL сервера, но их состояние хранится в памяти, поэтому их влияние на производительность минимально.
Как работают advisory‑locks в PostgreSQL
Каждая advisory‑блокировка идентифицируется 64‑битным ключом, который может быть передан как один bigint или как пара int4. При вызове функции сервер пытается установить эксклюзивную (или разделяемую) блокировку:
pg_advisory_lock(key)– блокирует ключ на уровне сессии (не откатывается при rollback).pg_advisory_xact_lock(key)– блокировка привязана к текущей транзакции и откатывается автоматически.pg_try_advisory_lock(key)– немедленно возвращаетtrueилиfalse, не блокируя процесс.
Важно помнить, что блокировки не наследуются между соединениями. Если соединение завершилось, все его advisory‑блокировки освобождаются автоматически.
Практические примеры использования
Рассмотрим несколько типовых сценариев, где advisory‑locks дают ощутимый выигрыш.
1. Координация фоновых задач
Допустим, у вас есть несколько воркеров, которые обрабатывают очередь задач из таблицы jobs. Чтобы два воркера не взяли одну и ту же задачу, можно использовать ключ, построенный из job_id:
SELECT pg_try_advisory_lock(job_id) FROM jobs WHERE status='pending' LIMIT 1;
Если функция вернула true, воркер получает эксклюзивный доступ к этой задаче и может менять её статус на «processing». В противном случае он переходит к следующей записи.
2. Защита критических секций в микросервисах
В распределённой системе несколько сервисов могут одновременно пытаться обновить кеш Redis. При помощи advisory‑locks можно гарантировать, что только один процесс будет выполнять дорогой запрос к базе:
BEGIN;
SELECT pg_advisory_xact_lock(123456); -- фиксированный ключ для конкретного кеша
-- Выполняем обновление кеша
COMMIT;
Если в момент выполнения уже есть активная блокировка, запрос будет ждать, но не будет блокировать строки в таблицах.
3. Управление миграциями схемы
В больших кластерах иногда требуется выполнить одноразовую миграцию, но только один из узлов должен её запустить. Пример:
SELECT pg_try_advisory_lock(987654321);
-- Если вернул true – запускаем миграцию
-- После завершения вызываем pg_advisory_unlock(987654321);
Таким образом, даже при одновременном старте нескольких экземпляров миграция выполнится единожды.
Проблемы и подводные камни
Хотя advisory‑locks удобны, у них есть ограничения, которые стоит учитывать:
- Отсутствие визуализации: стандартные инструменты
pg_stat_activityне показывают активные advisory‑блокировки. Их можно увидеть только через функцииpg_advisory_unlock_all()или запросы кpg_locksс типомadvisory. - Риск deadlock‑ов: если два процесса пытаются захватить несколько разных ключей в разном порядке, может возникнуть взаимная блокировка. Рекомендуется фиксировать порядок захвата.
- Память сервера: каждый активный ключ хранится в shared memory. При большом количестве одновременных блокировок (например, >10 000) может потребоваться увеличить параметр
max_locks_per_transaction, хотя advisory‑locks используют отдельный пул. - Только внутри кластера: advisory‑locks работают только в рамках одного PostgreSQL‑кластера. Если приложение распределено между несколькими кластерами, необходимо использовать внешние согласователи (например, etcd).
Лучшие практики и инструменты
Чтобы извлечь максимум из Advisory Locks, следуйте проверенным рекомендациям:
- Всегда используйте
pg_try_advisory_lockв коде, где блокировка не должна приводить к длительному ожиданию. - Привязывайте блокировки к транзакциям только когда они действительно нужны для атомарности.
- Логируйте ключи и результаты попыток захвата, чтобы упростить отладку.
- Регулярно проверяйте
pg_locksна предмет «зависших» advisory‑блокировок. - Для генерации уникальных ключей используйте хеши от бизнес‑идентификаторов (например,
hashtext('order:'||order_id)), чтобы избежать коллизий.
На портале toolbox-online.ru вы найдёте набор утилит, которые помогут визуализировать состояние advisory‑locks, генерировать безопасные ключи и автоматически освобождать «зависшие» блокировки.
Начните использовать наши инструменты уже сегодня и сделайте управление конкурентным доступом в PostgreSQL простым и надёжным!
Теги