Безопасно применять DDL с lock_timeout и повторными попытками (избегать очередей блокировок)
PostgreSQLСессииКоманда
-- Single attempt: give up if the lock is not acquired within 3 s instead of queueing and blocking traffic
SET lock_timeout = '3s';
ALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;
RESET lock_timeout;
-- Automatic retry: up to 5 attempts, 3 s apart
DO $$
DECLARE
attempt int := 0;
BEGIN
LOOP
attempt := attempt + 1;
BEGIN
SET LOCAL lock_timeout = '2s';
ALTER TABLE customer ADD COLUMN IF NOT EXISTS segment text;
RAISE NOTICE 'OK, attempt %', attempt;
EXIT;
EXCEPTION WHEN lock_not_available THEN
RAISE NOTICE 'lock not acquired, attempt %', attempt;
IF attempt >= 5 THEN RAISE; END IF;
PERFORM pg_sleep(3);
END;
END LOOP;
END $$;Что делает
Не даёт DDL, которому нужна ACCESS EXCLUSIVE, бесконечно ждать, если он не может получить блокировку, и блокировать каждый запрос, вставший за ним в очередь (очередь блокировок); повторяет попытки через короткие интервалы.
Когда использовать
При применении быстрых, но сильно блокирующих изменений, таких как ALTER TABLE, CREATE/DROP TRIGGER, ATTACH PARTITION или RENAME, на нагруженных таблицах в production.
Внимание
Без lock_timeout ALTER TABLE, вставший в очередь за одним долгим SELECT, заставляет ждать и все последующие чтения/записи и может за минуты исчерпать пул подключений. lock_timeout ограничивает только ожидание блокировки; он не ограничивает время выполнения DDL, перезаписывающего таблицу (например, изменение типа столбца). Блок DO — это одна транзакция, удерживающая снимок между попытками; для длительных сценариев повторов предпочтительнее внешний цикл в shell.
Связанные материалы
- Завершить backend (pg_terminate_backend)
- Отменить выполняющийся запрос (pg_cancel_backend)
- Показать активные и блокирующие запросы
- Построить дерево блокировок (найти корневой блокирующий процесс)
- Детально изучить ожидания блокировок по объектам и режимам блокировки (pg_locks)
- Найти долгие транзакции и транзакции в состоянии 'idle in transaction'