DBAtlas · Атлас эксплуатации баз данных

Безопасно применять 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.

Открыть в DBAtlas →

Связанные материалы