这是否属于死锁?并行执行嵌套存储过程出现互相阻塞
为什么SQL Server没自动检测并终止你的“互相阻塞”进程?
你遇到的这种情况其实很常见——两个SPID看起来互相阻塞,但SQL Server没触发死锁终止,核心原因是你看到的“互相阻塞”可能并不满足SQL Server定义的死锁条件。
先理清楚关键逻辑:SQL Server的死锁检测机制只会在满足四个必要死锁条件时才会介入,这四个条件缺一不可:
- 互斥:资源同一时间只能被一个线程持有
- 持有并等待:线程持有一个资源的同时,等待另一个资源
- 不可剥夺:持有的资源不能被强制夺走
- 循环等待:形成闭合的依赖链(比如A等B,B等A,或者更长的环)
回到你的场景,你提到“两者均有部分线程处于此状态”,这很可能是问题所在:
- 如果只是SPID 116的部分线程在等SPID 117的线程,同时SPID 117的另一部分线程在等SPID 116的线程,这两组等待是独立的,并没有形成完整的循环依赖闭环。比如SPID116的线程A持有锁X,等待锁Y;SPID117的线程B持有锁Y,等待锁Z;SPID116的线程C持有锁Z,等待锁X——这才是完整的死锁环。但如果只是A等B的锁,B等C的锁,而C没等任何人,那就只是普通阻塞,不是死锁。
- 嵌套存储过程的逻辑可能加剧这种情况:如果嵌套过程里的资源获取顺序在不同分支有差异,比如某些分支先锁表A再锁表B,另一些分支先锁B再锁A,但只有当两个SPID的线程刚好交叉获取这些锁时,才会形成死锁环。如果只是部分线程的等待链没闭环,SQL Server就不会判定为死锁。
另外,还有几个可能的细节:
- 锁的类型和范围:你设置了锁定提示,但如果锁是共享锁(S)和排他锁(X)的组合,比如SPID116持有S锁等待X锁,SPID117持有X锁等待S锁,这时候其实是会触发死锁的,但如果锁的范围不同(比如一个是行锁,一个是表锁),或者是键范围锁,可能死锁检测的逻辑需要更完整的闭环才能识别。
- 死锁检测的时机:SQL Server默认每5秒做一次死锁检测(可以通过
sp_configure 'deadlock priority'和跟踪标记调整),如果阻塞时间刚好短于检测间隔,可能还没触发检测,但你需要手动终止,说明阻塞时间足够长,那更可能是没满足死锁条件。
下一步排查建议
- 用
sys.dm_tran_locks动态管理视图查看两个SPID具体持有的锁资源和等待的锁资源,确认是否存在闭合的依赖链。 - 开启死锁跟踪:执行
DBCC TRACEON(1222, -1),这样下次出现类似情况时,SQL Server会把死锁信息写入错误日志,即使没自动终止,也能判断是否真的是死锁。 - 检查嵌套存储过程的资源获取顺序:确保所有分支里的锁获取顺序一致,这是避免死锁的核心手段之一。
内容的提问来源于stack exchange,提问作者deroby
相关产品推荐
相关产品推荐

