使用 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 循环。