pg_advisory_lock 正确使用方法

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;

你可能感兴趣的:(PostgreSQL,postgresql,PostgreSQL二次开发,常用技巧)