新加入的Advisory Locks似乎是个不错的特性
http://www.postgresql.org/docs/8.2/interactive/explicit-locking.html#ADVISORY-LOCKS
http://www.postgresql.org/docs/8.2/interactive/functions-admin.html#FUNCTIONS-ADVISORY-LOCKS
这里有一篇介绍的blog
http://merlinmoncure.blogspot.com/
个人的一些实验:
pgamdin III 1.6.1
Postgresql 8.2
windows XP
建立一个测试表
CREATE TABLE tests
(
id serial NOT NULL,
name character varying(32),
CONSTRAINT tests_pkey PRIMARY KEY(id)
)
WITHOUT OIDS;
测试数据
insert into tests(name) values ('dafei'),('feifei'),('afei');
[B]实验一:[/B]
分别打开两个SQL查询窗口A,B
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
然后在B中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
阻塞中。。。
然后在A中,输入
select pg_advisory_unlock(id),from tests where id = 1;
执行成功,返回 t
这个时候 ,B中语句执行成功,解除了阻塞。
[B]实验二:[/B]
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行两次,
均执行成功。
然后在B中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
阻塞中。。。
然后在A中,输入
select pg_advisory_unlock(id),from tests where id = 1;
执行成功,返回 t
但是B仍阻塞,
再次执行
select pg_advisory_unlock(id),from tests where id = 1;
执行成功,返回 t
这个时候 ,B中语句执行成功,解除了阻塞。
[B]实验三:[/B]
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
然后在B中输入
select pg_advisory_unlock(id),from tests where id = 1;
执行成功,返回 f
然后执行
select pg_advisory_lock(id),* from tests where id = 1;
执行 阻塞中。。。
然后在A中,输入
select pg_advisory_unlock(id),from tests where id = 1;
执行成功,返回 t
这个时候 ,B中语句执行成功,解除了阻塞。
[B]实验四:[/B]
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
然后在B中输入
select pg_advisory_lock(id),from tests where id = 2;
执行
执行成功。
[B]实验五:[/B]
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
然后再输入
select * from tests where id = 1;
执行成功
然后在B中输入
select * from tests where id = 1;
执行成功
[B]实验六:[/B]
在A中输入
begin;
select pg_advisory_lock(id),* from tests where id = 1;
commit;
执行
执行成功。
然后在B中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
阻塞中。。。
然后在A中,输入
select pg_advisory_unlock(id),from tests where id = 1;
执行成功,返回 t
这个时候 ,B中语句执行成功,解除了阻塞。
[B]实验七:[/B]
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
然后在B中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
阻塞中。。。
关闭A,这时B仍在阻塞中。
停止运行B,重新执行
select pg_advisory_lock(id),* from tests where id = 1;
执行成功。
[B]实验八:[/B]
在A中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
然后关闭A
然后在B中输入
select pg_advisory_lock(id),* from tests where id = 1;
执行
执行成功。
[B]对比实验一[/B]
使用相近的for update语句
在A中输入
select * from tests where id = 1 for update;
执行
执行成功。
在B中输入
select * from tests where id = 1 for update;
执行
执行成功。
[B]对比实验二[/B]
在A中输入
begin;
select * from tests where id = 1 for update;
执行
执行成功。
在B中输入
select * from tests where id = 1 for update;
执行
阻塞中。。。
然后在A中执行 commit;
执行成功 , 这个时候 ,B中语句执行成功,解除了阻塞。
[B]实验结果分析[/B]
从对比实验来看
for update所使用的锁 Row Share,会在事务结束时释放。
从实验六来看 advisory lock 不会在事务结束时释放(实验6)。
从实验来看,advisory lock 会在用户当前Session中有效,当会话结束时,会释放所有的advisory lock(实验七,实验八)
不过,从实验七来看,似乎如果其他会话仍在请求同一资源的话,并不回感应到锁定该资源的会话退出导致的advisory lock 释放,
必须下次请求时,才会感应到,不知道这个是不是 一个bug(这一点我的测试结果与本文原作者有些不同。假设A、B两个窗口进行测试,如果A先LOCK,然后B再LOCK,这时B会HANG住。这时如果A使用/q正常退出,那么B是会感应到的并且执行成功;但如果我使用CTRL+Z在A窗口强行退出,那么B是感知不到的,仍然阻塞,这时通过pg_locks仍然可以看到原先A窗口相应的进程在持有锁,通过OS命令KILL掉相应的进程,B就感知到了并执行LOCK成功。)
从实验来看advisory lock 不受事务的影响,在同意个会话中 advisory lock 可以多次lock,但是必须与unlock成对使用(实验二)。
advisory lock 不会造成同一个表被锁定(实验四)
advisory lock 不会阻塞一般的查询,包括for update语句。(两个窗口同时执行select ... for update还是会互锁的)(实验五)
个人感觉advisory lock 最大的优势在于与事务无关,或者 跨事务锁定。
对于 pg_advisory_lock_shared 没有进行详尽实验,
在两个会话中可以同时执行
select pg_advisory_lock_shared(id),* from tests;
但是某个会话中 执行 pg_advisory_lock_shared后,其他会话中执行pg_advisory_lock 会阻塞。
##########下面是转的另一篇关于ADVISORY LOCK的文章########