文章字数:411,阅读全文大约需要1分钟
sqlServer
数据库环境下出现CannotAcquireLockException
和LockAcquisitionException
异常,记录排查过程
排查过程
- 期初以为是接口执行时间过长,导致其它事务获取不到锁导致的。之后模拟锁表,结果发现锁了好几分钟的情况下其他事务都会自旋等待,直到获取到锁才会继续执行。
结果就是手动加锁只会让接口执行时间边长,并不会抛出上述异常。 - 搜索了一下上述异常,发现死锁才会抛出这个异常。之前的尝试最多是等待超时,死锁需要两个事物彼此等待。
- 还原发生异常的现场数据,并且运行了一下。发现下发命令的接口执行一分钟左右就会抛出异常。
- 使用客户端连接数据库,在程序运行时手动运行以下
sql
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17SELECT
der.[session_id],der.[blocking_session_id],
sp.lastwaittype,sp.hostname,sp.program_name,sp.loginame,
der.[start_time] AS '开始时间',
der.[status] AS '状态',
dest.[text] AS 'sql语句',
DB_NAME(der.[database_id]) AS '数据库名',
der.[wait_type] AS '等待资源类型',
der.[wait_time] AS '等待时间',
der.[wait_resource] AS '等待的资源',
der.[logical_reads] AS '逻辑读次数'
FROM sys.[dm_exec_requests] AS der
INNER JOIN master.dbo.sysprocesses AS sp ON der.session_id=sp.spid
CROSS APPLY sys.[dm_exec_sql_text](der.[sql_handle]) AS dest
--WHERE [session_id]>50 AND session_id<>@
ORDER BY der.[session_id]
GO - 最后在抛出异常前发现两个互锁的事务
session_id | blocking_session_id |
---|---|
1 | 2 |
2 | 1 |
- 通过查询出来的
session_id
和blocking_session_id
可以看出两个事物形成了死锁 - 再通过查询出的sql语句定位到具体代码(具体操作要看业务逻辑)
- 通过报错下的调用栈也可以定位到其中一条sql语句的位置。
分析
两张表同时被两个事物先后访问,并分别加锁。导致锁表