SQL Server不同行并发删除引发死锁问题求助
死锁场景分析与解决
死锁核心循环
从死锁XML和图形来看,三个事务形成了循环等待链:
- SPID67(process111c0a04e8):持有
Documento表主键索引上某行的X锁(lock112348fb80),等待另一主键键值的S锁(lock113213f500) - SPID68(processb8de6eca8):等待lock113213f500的S锁,而该资源被SPID72持有X锁
- SPID72(process1117804108):持有lock113213f500的X锁,等待SPID67持有的lock112348fb80的S锁
关键原因解析
你提到并发操作未针对同一行,但死锁依然发生,核心原因如下:
- 隐式事务的锁持有时长:所有事务都是隐式事务(
transactionname="implicit_transaction"),每个事务包含删除+跨表插入操作,锁会在整个事务周期内持续持有,大大增加了锁冲突概率。 - 删除操作的索引锁逻辑:即使通过主键
id精准删除单条记录,SQL Server在遍历B树索引时,会对路径上的非叶节点键加S锁用于定位目标行。如果并发删除的行在B树中共享同一个上层非叶节点,不同事务对这些节点的锁请求顺序相反时,就会形成死锁。 - 隔离级别不影响修改锁:Read Committed(RC)和Read Committed Snapshot(RCSI)的区别仅在于读操作是否加锁,但修改操作(删除)依然会对目标行加X锁,对索引路径加S锁,因此无法避免这类死锁。
解决方案建议
- 缩短事务时长:将隐式事务改为显式事务,在删除+插入操作完成后立即提交,减少锁的持有时间。示例:
BEGIN TRANSACTION DELETE FROM Documento WHERE id = @P0; -- 执行其他表插入操作 COMMIT TRANSACTION; - 统一锁请求顺序:如果事务中涉及多表操作,确保所有并发事务的操作顺序完全一致(比如先删除
Documento再插入其他表,所有事务都遵循这个顺序),避免交叉等待。 - 优化索引访问路径:检查主键索引的碎片化程度,定期重建或重组索引,减少SQL Server遍历索引时需要访问的非叶节点数量,降低锁冲突概率。
- 检查触发器/依赖操作:确认
Documento表是否存在删除触发器,若触发器中有额外的索引扫描或修改操作,会扩大锁范围,需优化触发器逻辑。
内容的提问来源于stack exchange,提问作者Tiago Schumann
相关产品推荐
相关产品推荐

