无显式事务时是否会发生死锁?SQL Server 2008场景问询
嘿,这两个问题问到点子上了,我来给你讲明白:
当然有可能!很多人误以为只有用BEGIN TRAN/END TRAN包裹的显式事务才会触发死锁,但实际上隐式事务——也就是数据库自动为单条DML语句创建的事务——同样会卷进死锁。
哪怕你只执行一条UPDATE/DELETE/INSERT,SQL Server都会在后台自动开启一个事务,执行完语句后立刻提交。这个过程你看不到,但锁的申请、持有和释放逻辑和显式事务完全一致。只要多个这样的隐式事务互相持有对方需要的锁,并且形成循环等待的局面,死锁就必然发生。
绝对可以!单条DML语句本身就是一个独立的隐式事务,只要锁的获取顺序不对,死锁很容易模拟出来。我给你一个可复现的示例:
第一步:准备测试环境
先创建测试表、插入数据并建立辅助索引:
CREATE TABLE TestDeadlock ( ID INT PRIMARY KEY IDENTITY(1,1), Content VARCHAR(50) NOT NULL ); -- 插入1000条测试数据 INSERT INTO TestDeadlock (Content) SELECT 'Row_' + CAST(n AS VARCHAR) FROM (SELECT TOP 1000 ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS n FROM sys.all_columns) AS t; -- 创建非聚集索引,让UPDATE可以按不同顺序扫描行 CREATE NONCLUSTERED INDEX IX_TestDeadlock_Content ON TestDeadlock(Content);
第二步:模拟死锁场景
打开两个独立的查询窗口(模拟两个会话Session 1和Session 2),先确保都处于默认的自动提交模式:
SET IMPLICIT_TRANSACTIONS OFF;
然后几乎同时执行以下两条语句:
- Session 1执行:
-- 按ID降序扫描并更新,先锁定大ID的行 UPDATE TestDeadlock SET Content = 'Updated_By_Session1_' + CAST(ID AS VARCHAR) WHERE Content LIKE 'Row_%' ORDER BY ID DESC;
- Session 2执行:
-- 按ID升序扫描并更新,先锁定小ID的行 UPDATE TestDeadlock SET Content = 'Updated_By_Session2_' + CAST(ID AS VARCHAR) WHERE Content LIKE 'Row_%' ORDER BY ID ASC;
不出意外的话,其中一个会话会立刻收到死锁错误提示:
消息 1205,级别 13,状态 51,第 1 行
事务(进程 ID XX)与另一个进程被死锁在 锁 资源上,并且已被选作死锁牺牲品。请重新运行该事务。
原理说明
两条单语句的隐式事务在执行过程中,分别持有了对方需要的锁:Session 1锁了大ID范围的行,Session 2锁了小ID范围的行,当双方需要跨越自己的锁范围继续更新时,就会形成循环等待——完全满足死锁的四个核心条件(互斥、持有并等待、不可剥夺、循环等待)。
除此之外,还有其他场景会触发这类死锁:比如两个会话分别执行INSERT SELECT,互相从对方要写入的表读取数据,同时持有对方需要的锁,最终也会形成死锁,全程不需要显式事务块。
内容的提问来源于stack exchange,提问作者Jamo

