← 中文资源

简体中文版本

PostgreSQL 咨询锁临界区

用咨询锁跨应用实例串行化临界区:try 版立即返回不阻塞、事务级锁随提交自动释放、双键形式按实体粒度加锁。

阅读英文原文

示例说明

定时任务部署多份就会跑多份——数据库的咨询锁给跨实例互斥提供原语:锁与任何表无关,纯逻辑编号。两种形态各有场景: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';