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 结构中,确保连接归还池时锁彻底归还。