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

SQL事务未触发死锁的原因排查:预期死锁却正常执行

你的思路存在这几个关键错误:

1. 错误认为UPDATE会锁定整张表

默认情况下,SQL Server的UPDATE操作只会对被修改的行加排他锁(X锁),而非锁定整个表——只有当表没有合适索引、执行计划触发全表扫描时,才会升级为表锁。所以事务1并没有锁定整个Journals表,事务2也没有锁定整个Books表,两者的锁仅作用于各自表内的部分行,互不冲突。

2. 错误认为SELECT会被UPDATE的排他锁持续阻塞

在SQL Server默认的READ COMMITTED隔离级别下:

  • 若开启了READ_COMMITTED_SNAPSHOT(多数环境默认开启),SELECT会读取行的快照版本,完全不会被排他锁阻塞,直接获取事务提交前的旧数据。
  • 即使未开启快照,SELECT会对读取的行加共享锁(S锁),但共享锁与排他锁的冲突仅发生在同一行上。如果事务2只锁定了Books里的部分行,事务1的SELECT可正常读取其他未锁定的行;就算读到被锁的行,也只会等待事务2提交(事务2会在7秒后自动提交,等待后即可继续,不会形成死锁)。

3. 错误判断了死锁的触发条件

死锁的核心是循环等待:事务1持有锁A,等待事务2的锁B;同时事务2持有锁B,等待事务1的锁A。但你的场景里:

  • 事务1持有的是Journals行的X锁,它读取Books时不需要持有Books的X锁,最多是S锁(甚至快照读不需要锁);
  • 事务2持有的是Books行的X锁,它读取Journals时同样不需要持有Journals的X锁。
    两者不存在互相等待对方排他锁的情况,自然不会触发死锁。

如果要触发死锁,你需要让两个事务都先持有一个表的排他锁,再去请求另一个表的排他锁——比如把事务里的SELECT改成UPDATE,这样才会形成循环等待:

-- 事务1
BEGIN TRAN 
UPDATE Journals SET Pages = 20 
WAITFOR DELAY '00:00:07'
UPDATE Books SET Pages = 30 
Commit;
-- 事务2
BEGIN TRAN  
UPDATE Books SET Pages = 30 
WAITFOR DELAY '00:00:07'
UPDATE Journals SET Pages = 20 
Commit;

内容的提问来源于stack exchange,提问作者Sergey Dev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 11:52:03