这几天线上频繁报如下的错误:“无法释放应用程序锁(数据库主体: 'public',资源: 'aa'),原因是当前没有保留该应用程序锁。”
下面是写法:
1 declare @result int; 2 BEGIN TRANSACTION 3 EXEC @result= sp_getapplock @Resource = N'aa',@LockMode = 'Exclusive',@LockTimeout=5000; 4 if @result=-3 5 BEGIN 6 ROLLBACK TRANSACTION; 7 END 8 ELSE 9 BEGIN 10 select @result; 11 EXEC SP_EXECUTESQL N'update xxx set f_date=f_date' 12 EXEC sp_releaseapplock @Resource = N'aa'; 13 COMMIT TRANSACTION 14 15 END
看代码第一反应能够发现,如果@result是其他值或者加锁失败,会不会导致问题的发生,于是尝试了一下(手动滑稽)
结果还真是,如果资源没申请成功会导致下面的报错。 下面为测试代码:
1 declare @result int; 2 BEGIN TRANSACTION 3 --EXEC @result= sp_getapplock @Resource = N'aa',@LockMode = 'Exclusive',@LockTimeout=5000; 4 --if @result=-3 5 --BEGIN 6 -- ROLLBACK TRANSACTION; 7 --END 8 --ELSE 9 BEGIN 10 select @result; 11 EXEC SP_EXECUTESQL N'update xxx set f_date=f_date' 12 EXEC sp_releaseapplock @Resource = N'aa'; 13 COMMIT TRANSACTION 14 15 END
所以上面的问题,可以记录下@rsult值是其他的情况下,是否也需要进行rollback。
参考链接:
官网提供的例子是可以>=0 为成功,<0为失败。
而代码例子的结构和我贴的结构几乎一致,所以开发人员当时应该是直接copy写法以后,然后就用了,完全没想到有什么其他的坑;→_→
总结:
解决问题的过程很简单也很容易,但能够发现就是一个好事,侧面说明了看文档的重要性。
-----追加
验证资源锁是否存在
1 declare @resource nvarchar(255) ='aa' --资源字符串 2 declare @key nvarchar(257)='['+@resource+']'; 3 IF exists( 4 select 1 from sys.dm_tran_locks 5 where 6 resource_type=N'APPLICATION' 7 AND request_mode=N'X' 8 and CHARINDEX(@key,resource_description) >0 9 and resource_database_id=db_id() 10 ) 11 begin 12 print ('已存在'); 13 end 14 else 15 begin 16 print ('不存在'); 17 end