postgresql Advisory Locks
新加入的Advisory Locks似乎是个不错的特性
##########下面是转的另一篇关于ADVISORY LOCK的文章########
PostgreSQL 9.1 新增了一个事务级的advisory lock,原来只有SESSION级的。
事务级别的advisory lock不能显性的释放。
advisory lock和系统的MVCC不是一个概念,基本上完全不相干。锁的数量由
max_locks_per_transactionandmax_connections决定。
advisory lock可以在pg_locks视图里面看到。
SESSION级的advisory lock一旦获取,需要手工释放或者SESSION 结束后自动释放。SESSION级别的advisory lock还有一个和事务锁不一样的风格。
如:
1. 在一个begin; end;之间获取SESSION级别的advisory lock时,事务被回滚
或者事务FAILED
的话SESSION advisory锁不会被回滚。
digoal=> begin;
BEGIN
digoal=> select pg_advisory_lock(1);
pg_advisory_lock
------------------
(1 row)
digoal=> rollback;
ROLLBACK
digoal=> select * from pg_locks where objid=1;
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid
| mode | granted
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----
--+---------------+---------
advisory | 16386 | | | | | | 0 | 1 | 1 | 2/9186 | 2598
8 | ExclusiveLock | t
(1 row)
2. 在一个begin; end;之间释放SESSION级别的advisory lock时,事务被回滚或者事务FAILED的话SESSION advisory锁仍被释放。
digoal=> select pg_advisory_unlock(1);
pg_advisory_unlock
--------------------
t
| mode | granted
+------+---------
(0 rows)
3. 同一个session 级别的advisory lock可以在一个SESSION里面多次获取,但是释放也要多次释放。
如下,获取两次,释放两次。
digoal=> select pg_advisory_lock(1);
pg_advisory_lock
------------------
(1 row)
digoal=> select pg_advisory_lock(1);
pg_advisory_lock
------------------
(1 row)
digoal=> select pg_advisory_unlock(1);
pg_advisory_unlock
--------------------
t
(1 row)
digoal=> select * from pg_locks where objid=1;
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid
| mode | granted
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----
--+---------------+---------
advisory | 16386 | | | | | | 0 | 1 | 1 | 2/9198 | 2598
8 | ExclusiveLock | t
(1 row)
digoal=> select pg_advisory_unlock(1);
pg_advisory_unlock
--------------------
t
(1 row)
digoal=> select * from pg_locks where objid=1;
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid
| mode | granted
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----
+------+---------
(0 rows)
4. session和transaction 级别的lock在同一个SESSION中共用锁空间,所以在TRANSACTION中获取到了锁,在同一个SESSION中也可以获取到。另一个SESSION就获取不到。TRANSACTION ADVISORY LOCK不可以显性释放,会自动在事务结束(包含提交或回滚,正常或不正常)后释放。
如下
SESSION A:
digoal=> begin;
BEGIN
digoal=> select pg_advisory_xact_lock(1);
pg_advisory_xact_lock
-----------------------
(1 row)
这里获取到事务级的ADVISORY 锁。
digoal=> select pg_advisory_lock(1);
pg_advisory_lock
------------------
(1 row)
这里获取到SESSION ADVISORY LOCK。
digoal=> end;
COMMIT
这里自动释放了TRANSACTION ADVISORY LOCK。
SESSION B:
digoal=> select pg_advisory_lock(1);
等待中。。。
SESSION A:
digoal=> select * from pg_locks where objid=1;
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid
| mode | granted
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----
--+---------------+---------
advisory | 16386 | | | | | | 0 | 1 | 1 | 2/9202 | 2598
8 | ExclusiveLock | t
advisory | 16386 | | | | | | 0 | 1 | 1 | 4/76 | 2645
0 | ExclusiveLock | f
(2 rows)
看到SESSION B在等待这个锁。
digoal=> select pg_advisory_unlock(1);
pg_advisory_unlock
--------------------
t
(1 row)
这里释放了SESSION A 的SESSION ADVISORY LOCK。
locktype | database | relation | page | tuple | virtualxid | transactionid | classid | objid | objsubid | virtualtransaction | pid
| mode | granted
----------+----------+----------+------+-------+------------+---------------+---------+-------+----------+--------------------+-----
--+---------------+---------
advisory | 16386 | | | | | | 0 | 1 | 1 | 4/0 | 2645
0 | ExclusiveLock | t
(1 row)
可以看到B已经获得了这个锁。
由于advisory lock和MVCC不相干。所以不存在和MVCC锁的冲突。适合特殊场景的应用,降低锁冲突或者长时间持锁带来的数据库(如VACUUM释放空间,这个我很久前的BLOG有写过)压力。
advisory lock的应用场景举例(应用控制的锁):
比如数据库里面存储了文件和ID的对应关系,应用程序需要长时间得获得一个锁,然后对文件进行修改,再释放锁。
测试数据:
digoal=> create table tbl_file_info (id int primary key,file_path text);
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "tbl_file_info_pkey" for table "tbl_file_info"
CREATE TABLE
digoal=> insert into tbl_file_info values (1,'/home/postgres/advisory_lock_1.txt');
INSERT 0 1
digoal=> insert into tbl_file_info values (2,'/home/postgres/advisory_lock_2.txt');
INSERT 0 1
digoal=> insert into tbl_file_info values (3,'/home/postgres/advisory_lock_3.txt');
INSERT 0 1
SESSION A:
digoal=> select pg_advisory_lock(id),file_path from tbl_file_info where id=1;
pg_advisory_lock | file_path
------------------+------------------------------------
| /home/postgres/advisory_lock_1.txt
(1 row)
应用程序对/home/postgres/advisory_lock_1.txt文件进行编辑之后,再释放这个advisory锁。
SESSION B:
当SESSIONA在编辑
/home/postgres/advisory_lock_1.txt这个文件的时候,无法获得这个锁,所以可以确保不会同时编辑这个文件。
如果不使用advisory lock,改用MVCC,来看看如何 :
仍旧使用以上的测试数据,
SESSION A:
digoal=> begin;
BEGIN
digoal=> select file_path from tbl_file_info where id=1 for update;
file_path
------------------------------------
/home/postgres/advisory_lock_1.txt
(1 row)
应用程序对/home/postgres/advisory_lock_1.txt文件进行编辑之后,再释放这个锁。
digoal=> end;
COMMIT
因此这个SESSION在编辑文件的时候,需要保持这个事务,一方面降低了数据库使用的并发性,另一方面带来了长事务,会有回收不了DEAD TUPLE空间的问题,参考我以前写过的BLOG。
http://blog.163.com/digoal@126/blog/static/163877040201011162912604/
DETAIL: xxxx dead row versions cannot be removed yet.
SESSION B:
当SESSIONA在编辑
/home/postgres/advisory_lock_1.txt 这个文件的时候,如果需要更改同一个文件,同样,先获得锁然后操作。
digoal=> select file_path from tbl_file_info where id=1 for update;
等待SESSION A释放锁。
【参考】
http://www.postgresql.org/docs/9.1/static/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
http://www.postgresql.org/docs/9.1/static/explicit-locking.html#ADVISORY-LOCKS
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |