pg_advisory_lock和pg_advisory_unlock是配对使用的,但是如果之间的部分出现异常时,可能会导致不能正确的调用pg_advisory_unlock.例如:
create or replace function test_insert_advisory(integer)
returns integer
as $$
declare
begin
perform pg_advisory_lock($1::bigint);
insert test(id) values($1);
perform pg_advisory_unlock($1::bigint);
return $1;
end;
$$ language plpgsql strict;
虽然看起来没什么问题,但是如果insert异常时不会释放锁,就需要靠dba手动检查释放了.
改进方法
使用bit标志位来判断,操作锁的同时操作标志位,位操作函数定义如下:
/****************************************************************************************
将第x个bit位设置为1,ibit的值范围0-31
00000000000000000000000000000000
****************************************************************************************/
create or replace function SE_flag_add(iflag integer, ibit integer)
returns integer
as $$
select iflag + (1<<ibit);
$$ language sql strict;
/****************************************************************************************
将第x个bit位设置为0,ibit的值范围0-31
00000000000000000000000000000000
****************************************************************************************/
create or replace function SE_flag_remove(iflag integer, ibit integer)
returns integer
as $$
select iflag - (1<<ibit);
$$ language sql strict;
/****************************************************************************************
判断第x个bit位是否为1,ibit的值范围0-31
00000000000000000000000000000000
****************************************************************************************/
create or replace function SE_flag_bitisone(iflag integer, ibit integer)
returns boolean
as $$
select (case ((iflag >> ibit) & 1) when 1 then
true
else
false
end);
$$ language sql strict;
改进刚才的函数
加锁和释放时同时操作锁和标志位,当异常时根据标志位来确定是否释放锁.
create or replace function test_insert_advisory(integer)
returns integer
as $$
declare
v_flag integer;
begin
v_flag := 0;
select pg_advisory_lock($1::bigint),SE_flag_add(v_flag,0);
--多次锁定是只要设置不同的bit位即可
--select pg_advisory_lock(345),SE_flag_add(v_flag,1);
--不管insert正确与否都保证释放锁
insert test(id) values($1);
--select pg_advisory_unlock(345),SE_flag_remove(v_flag,1);
select pg_advisory_unlock($1::bigint),SE_flag_remove(v_flag,0);
return $1;
exception
when others then
/*if(SE_flag_bitisone(v_flag,1) ) then
perform pg_advisory_unlock(345);
end if;*/
if(SE_flag_bitisone(v_flag,0) ) then
perform pg_advisory_unlock($1::bigint);
end if;
end;
$$ language plpgsql strict;