示例说明
定时任务部署多份就会跑多份——数据库的咨询锁给跨实例互斥提供原语:锁与任何表无关,纯逻辑编号。两种形态各有场景:pg_advisory_xact_lock 绑定事务,提交回滚自动释放,忘不掉;会话级的 try 版立即返回真假,适合主动跳过重复执行的调度器。
技术要点
- 事务级锁随提交自动释放
- try 变体立即返回不阻塞
- 双键编号支持实体级粒度
代码解读
开头的 try 锁演示占用与拒绝两态;核心示例在事务内取事务级锁后更新对账运行表——锁保证同一时刻只有一个实例在跑,事务提交时锁自动归还,崩溃回滚同样释放无死锁残留。双键形式把类目号与实体号组合,锁粒度从全局细化到单实体。
使用建议
锁编号要有注册表防多个业务撞号。会话级锁要求连接归还时必然 unlock,连接池要配重置语句。锁不是排队——需要等待语义用带超时的循环重试。
代码示例
-- PostgreSQL 会话级咨询锁:try 锁拿不到立即返回假
SELECT pg_try_advisory_lock(4271);
-- 若返回 true,此会话独占编号 4271 的锁
-- 其他实例的尝试会得到 false:
SELECT pg_try_advisory_lock(4271); -- false:已被占用
-- 临界区:只有一个实例在执行对账
BEGIN;
SELECT pg_advisory_xact_lock(4271); -- 事务级:提交或回滚自动释放
UPDATE reconciliation_runs
SET started_at = now(), status = 'running'
WHERE id = 'nightly-2026-10-21';
-- ... 对账主体 ...
UPDATE reconciliation_runs
SET finished_at = now(), status = 'done'
WHERE id = 'nightly-2026-10-21';
COMMIT; -- 锁随事务结束自动归还
-- 会话级锁的显式释放路径
SELECT pg_advisory_unlock(4271);
-- 双键形式:类目 ID 加实体 ID 组合编号
SELECT pg_try_advisory_lock(4271, 42);
-- 锁状态观测
SELECT pg_locks.locktype, pg_locks.objid, pg_locks.granted
FROM pg_locks
WHERE locktype = 'advisory';