PostgreSQL pg_advisory_lock() 函数使用指南
PostgreSQL pg_advisory_lock() 是数据库提供的高性能应用程序级咨询排他锁(Advisory Exclusive Lock)。
在处理复杂的分布式业务(例如跨微服务防重、单据序列生成、批量对账防并发碰撞)时,如果通过对数据库行执行 SELECT ... FOR UPDATE 加锁,往往会带来行版本膨胀、死锁冲突以及必须绑定在长事务中的局限性。
pg_advisory_lock() 允许应用程序自主定义 64 位整型数字标识作为锁标识符。该锁直接在 PostgreSQL 的共享内存锁管理器中分配,不触碰任何物理表或磁盘,因此开销极低、执行极快。
PostgreSQL pg_advisory_lock() 语法
这里是 PostgreSQL pg_advisory_lock() 函数的标准语法:
-- 单个 64 位整型参数
pg_advisory_lock(key bigint)
-- 两个 32 位整型参数
pg_advisory_lock(key1 integer, key2 integer)
参数
key- 64 位大整数(
bigint),表示应用程序自定义的全局锁标识。 key1,key2- 两个 32 位整数(
integer)。常用于分层命名空间加锁(例如key1代表业务表类型标识,key2代表业务实体主键 ID)。
返回值
返回类型为 void:
- 调用该函数会阻塞等待,直到成功获取到独占排他锁为止。
- 若需要非阻塞试探,应使用其同胞函数
pg_try_advisory_lock()。
PostgreSQL pg_advisory_lock() 示例
以下示例演示会话级咨询锁的加锁、冲突等待与显式释放流程。
基础加锁与显式解锁
在会话 1 中针对标识为 998877 的核心任务申请排他锁:
-- 1. 获取会话级咨询排他锁
SELECT pg_advisory_lock(998877);
-- 2. 执行排他关键业务逻辑...
-- 3. 主动释放锁
SELECT pg_advisory_unlock(998877) AS is_unlocked;
输出:
is_unlocked
-------------
t返回 t(true)表示锁已成功解开。
双参数分层命名加锁模式
在复杂的电商订单系统中,利用双整型模式分别锁定业务类型与订单编号:
-- 约定 1001 代表订单模块,50240 代表订单编号
SELECT pg_advisory_lock(1001, 50240);
-- 处理完毕后对应释放
SELECT pg_advisory_unlock(1001, 50240);
查看当前活跃的咨询锁
运维人员可以通过系统视图 pg_locks 实时监控当前库中所有的咨询锁:
SELECT
pid,
locktype,
mode,
granted,
classid,
objid
FROM pg_locks
WHERE locktype = 'advisory';
输出:
pid | locktype | mode | granted | classid | objid
-------+----------+---------------+---------+---------+-------
24108 | advisory | ExclusiveLock | t | 1001 | 50240结果清晰展现了进程 24108 正在持有 (1001, 50240) 排他锁,为定位阻塞提供了详实依据。
结论
- 会话级 vs 事务级:
pg_advisory_lock()是会话级的(Session-level),事务提交(COMMIT)或回滚(ROLLBACK)不会释放它!必须显式调用pg_advisory_unlock()或等待会话断开。如果希望锁随事务自动释放,请改用pg_advisory_xact_lock()。 - 锁重入计数:在同一个会话中对同一个 key 连续调用两次
pg_advisory_lock()会使锁计数变为 2,必须对应调用两次pg_advisory_unlock()才能真正解开。 - 在应用程序中,始终将
pg_advisory_unlock()置于finally结构中,确保连接归还池时锁彻底归还。