DBAtlas · 数据库运维图谱

使用 lock_timeout 和重试安全地执行 DDL(避免锁队列)

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 $$;

作用

防止需要 ACCESS EXCLUSIVE 的 DDL 在拿不到锁时无限等待,并阻塞排在它后面的所有查询(锁队列);以短间隔重试。

适用场景

在生产环境繁忙的表上执行 ALTER TABLE、CREATE/DROP TRIGGER、ATTACH PARTITION 或 RENAME 等快速但锁定很重的变更时。

注意

没有 lock_timeout 时,排在单个长 SELECT 后面的 ALTER TABLE 也会使之后的所有读写等待,几分钟内就可能耗尽连接池。lock_timeout 只限制锁等待;它不限制重写表的 DDL(例如修改列类型)的运行时间。DO 块是单个事务,会在多次尝试之间持有快照;对于长时间重试的场景,优先使用外部 shell 循环。

在 DBAtlas 中打开 →

相关内容