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)voidsession级别排他锁
pg_advisory_lock(key1 int, key2 int)voidsession级别排他锁
pg_advisory_lock_shared(key bigint)voidsession级别共享锁
pg_advisory_lock_shared(key1 int, key2 int)voidsession级别共享锁
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)

四、咨询锁常见问题

  1. 当连接中断后,数据库的会话就会被中止,其持有的咨询锁也会被释放。
  2. 当事务提交或者回滚,持有的 session 级咨询锁不会被释放。
  3. 当一个 session 持有一把事务级的 key 值为 1 的锁,另外一个 session 不能持有 key 为 1 的 session 级的咨询锁。
  4. session 级别的排他锁,调用了几次 lock 函数,在释放时,也需要调用同样次数的 unlock 函数,才能真正释放。
  5. 在一个 64 位数字上加锁,永远不会阻塞两个 32 位数字上的锁,即使二者的数值一样,他们表示不同的空间。

文章评论

0条评论