一、什么是咨询锁?
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)
四、咨询锁常见问题
- 当连接中断后,数据库的会话就会被中止,其持有的咨询锁也会被释放。
- 当事务提交或者回滚,持有的 session 级咨询锁不会被释放。
- 当一个 session 持有一把事务级的 key 值为 1 的锁,另外一个 session 不能持有 key 为 1 的 session 级的咨询锁。
- session 级别的排他锁,调用了几次 lock 函数,在释放时,也需要调用同样次数的 unlock 函数,才能真正释放。
- 在一个 64 位数字上加锁,永远不会阻塞两个 32 位数字上的锁,即使二者的数值一样,他们表示不同的空间。