PostgreSQL咨询锁(advisory lock)

一、什么是咨询锁?

PostgreSQL 支持创建咨询锁(advisory lock),该锁与数据库本身没有关系,其含义由应用来定义,咨询锁能够让 PostgreSQL 变成一个锁服务提供中心,为应用对一些非数据库资源并发访问提供控制。当然,也可以通过 select * from tb where id=xxx for update 来实现同样的功能,但是咨询锁因为与具体的数据没有关系,能够提供更好的性能。

PostgreSQL 咨询锁能够实现很多分布式系统中类似 Zookeeper 的锁服务,如在分布式系统中,多台服务器需要竞争,从而使自己成为 master,就可以通过竞争咨询锁来达成,谁得到咨询锁,谁就成为 master。

咨询锁支持会话级别和事务级别,会话级别的咨询锁,在会话结束时会自动释放,事务级别的咨询锁,在事务结束时自动释放。

二、咨询锁相关函数

咨询锁用一个 64 bit 的数字或者两个 32 bit 的数字来表示,并提供了一些函数来实现加锁和释放锁的操作,函数如下:

函数名称 返回类型 描述
pg_advisory_lock(key bigint) void session级别排他锁
pg_advisory_lock(key1 int, key2 int) void session级别排他锁
pg_advisory_lock_shared(key bigint) void session级别共享锁
pg_advisory_lock_shared(key1 int, key2 int) void session级别共享锁
pg_advisory_unlock(key bigint) boolean 释放session级别排他锁
pg_advisory_unlock(key1 int, key2 int) boolean 释放session级别排他锁
pg_advisory_unlock_all() void 释放本session持有的所有session级别的锁
pg_advisory_unlock_shared(key bigint) boolean 释放session级别共享锁
pg_advisory_unlock_shared(key1 int, key2 int) boolean 释放session级别共享锁
pg_advisory_xact_lock(key bigint) void 获取事务级别排他锁
pg_advisory_xact_lock(key1 int, key2 int) void 获取事务级别排他锁
pg_advisory_xact_lock_shared(key bigint) void 获取事务级别共享锁
pg_advisory_xact_lock_shared(key1 int, key2 int) void 获取事务级别共享锁
pg_try_advisory_lock(key bigint) boolean 试图获取session级别排他锁,成功返回true,否则返回false
pg_try_advisory_lock(key1 int, key2 int) boolean 试图获取session级别排他锁,成功返回true,否则返回false
pg_try_advisory_lock_shared(key bigint) boolean 试图获取session级别共享锁,成功返回true,否则返回false
pg_try_advisory_lock_shared(key1 int, key2 int) boolean 试图获取session级别共享锁,成功返回true,否则返回false
pg_try_advisory_xact_lock(key bigint) boolean 试图获取事务级别排他锁,成功返回true,否则返回false
pg_try_advisory_xact_lock(key1 int, key2 int) boolean 试图获取事务级别排他锁,成功返回true,否则返回false
pg_try_advisory_xact_lock_shared(key bigint) boolean 试图获取事务级别共享锁,成功返回true,否则返回false
pg_try_advisory_xact_lock_shared(key1 int, key2 int) boolean 试图获取事务级别共享锁,成功返回true,否则返回false

根据函数名称中的关键字,就能大概判断出函数的功能:

  • lock 为加锁。
  • unlock 释放锁。
  • xact 为事务级别的咨询锁,不包含 xact 为 session 级别的咨询锁。事务级别的咨询锁只有加锁函数,没有 unlock 函数,因为事务锁在事务结束后自动释放。
  • try 尝试加锁,不管是否能获得锁,都立即返回,不会一直等待。
  • 根据参数类型分为不同的函数,int 表示 32 位数字,bigint 表示 64 位数字。

三、咨询锁使用示例

以下展示了两个会话,获取 session 级排他咨询锁的示例:

session1: pg=# select pg_advisory_lock(1); # session1 加锁 pg_advisory_lock ------------------ (1 row) session2: pg=# select pg_advisory_lock(1); # session2 阻塞 session1: pg=# select pg_advisory_unlock(1); # session1 释放锁 pg_advisory_unlock -------------------- t (1 row) session2: pg=# select pg_advisory_lock(1); # session2 获得锁 pg_advisory_lock ------------------ (1 row)