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
相关产品推荐
相关产品推荐

