You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL Server死锁触发条件及多客户端并发操作死锁错误解决方案

SQL Server 死锁处理核心问题解答

一、单会话查询被其他会话锁定的表,是否会触发死锁

单纯的单向锁等待场景不会触发死锁。
如果会话A查询的表被会话B持有排他锁,会话A只需要等待B释放锁、自身没有持有任何需要B申请的资源,这种场景属于阻塞,不属于死锁范畴——因为不存在“双方互相等待对方释放锁”的循环等待链。
只有满足以下条件时,才会在这类场景下触发死锁:

  • 会话A在执行查询的过程中,因为执行逻辑需要申请会话B已经持有的排他锁,进入等待队列
  • 同时会话B在持有对应锁的事务流程中,也需要申请会话A已经提前持有的其他资源的锁
  • 两者形成闭环的互相等待,被SQL Server锁监视器判定为死锁,引擎会自动选择事务开销更小的会话作为牺牲品终止执行

举个实际触发的典型场景:

  • 会话1开启事务后先更新用户表的某行,持有该行的排他锁,未提交事务
  • 会话2查询关联的订单表数据时,因为查询条件没有匹配的索引,需要申请订单表的意向排他锁完成全表扫描
  • 而订单表上对应数据行的排他锁,刚好是会话1之前操作时持有的
  • 此时会话2需要等会话1释放订单表的锁,会话1又需要等会话2释放用户表的共享锁,就会直接触发死锁

二、多终端并发执行增删改操作时,死锁类SQL错误的解决方法

多客户端并发操作场景下的死锁,本质是不同会话的事务对锁资源的申请顺序交叉,形成循环等待链,最终触发SQL Server抛出“事务与其他进程死锁,已被选为死锁牺牲品”的错误。可通过以下标准化流程从根源减少、消除死锁:

  • 统一所有事务的资源访问顺序:这是投入产出比最高的优化手段。所有涉及多表操作的事务,必须严格按照固定的表访问顺序执行(比如固定按照“用户表→订单表→支付表”的顺序访问),杜绝部分事务先访问订单表再访问用户表、另一部分事务反过来的交叉逻辑,从结构上打破循环等待的形成条件
  • 为高频增删改、查询的过滤条件配置适配的非聚集索引:很多死锁的诱因是SQL执行时没有走索引,不得不扫描大范围的行、页,申请的锁数量远超过实际需要的范围,大大增加和其他会话锁资源交叉的概率。比如执行DELETE FROM Order WHERE CreateTime < '2024-01-01'时,如果没有针对CreateTime列的索引,SQL会扫描全表所有行,持锁范围过大,极易和其他会话的更新操作形成交叉等待
  • 压缩事务的持锁窗口:把大事务拆分为最小的原子操作单元,事务范围内只保留必须原子执行的数据库操作,禁止在事务块内嵌入外部接口调用、循环计算、人工交互等待等非数据库逻辑,尽可能缩短单个会话持锁的时间,减少其他会话撞入锁等待队列的概率
  • 结合业务场景优化锁策略:对一致性要求不高的读操作场景,可以启用READ COMMITTED SNAPSHOT ISOLATION隔离级别,用行版本存储替代读操作阶段的共享锁,从根本上避免“读堵写、写堵读”的交叉死锁;对批量操作场景,可以适当调整锁升级阈值,避免小事务因为锁数量累积升级为表锁,造成大范围的锁阻塞
  • 搭建常态化的死锁监控链路:通过SQL Server扩展事件配置死锁跟踪,将每次死锁发生时的会话执行SQL、持有的锁资源、等待的锁关系全量记录留存,出现死锁报错时可以直接定位问题SQL的逻辑缺陷,不需要临时抓包排查

注意:不要盲目调整锁超时参数作为死锁的解决方案。锁超时机制只是让等待锁的会话达到超时时间后主动报错退出,并没有打破循环等待的死锁形成逻辑,只能降低阻塞影响范围,无法真正消除死锁。


内容的提问来源于stack exchange,提问作者Ky Tan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.23 10:36:05