where 子句列中没有聚簇索引时并行更新导致死锁
Parallel updates causing deadlock when no clustered index in where clause column
我们遇到了这样一种情况,当尝试在同一事务中两次从两个同时连接更新 table 时发生死锁,并且每次查询 运行 时都可重现 运行 on 2 query windows 在 SSMS 中。 (AccountId 列是非聚集键)
见下文。
在 AccountId 列上创建聚集键后,死锁不再发生。是什么导致了这种行为?
如果 AccountId 上没有聚集索引并且此列上没有聚集索引,SQL 服务器必须锁定索引键然后锁定行。
所以第一次更新将会成功,更新后您将只有一行锁定在 table。
第二次更新将尝试锁定此行,并将等待第一次更新释放锁。它将能够获得索引上的键锁。
第三次更新将尝试锁定索引键,并将等待第二次更新释放锁。死锁。
我能够使用以下方法重现它 table:
create table test5 (x int,y int)
insert into test5 values (10,15)
GO
insert into test5 values (11,15)
GO 10000
create index ix on test5(x)
select * from test5
begin transaction
update test5
set y = 5
where x = 10
-- wait here
update test5
set y = 5
where x = 10
rollback
执行计划有一个非聚集索引查找输出基本 table RID (Bmk1000
) 和一个使用 RID 查找的 UPDATE
运算符。这提供了死锁中涉及的两个资源(非聚集索引键和与 RID 对应的基本 table 行)。
- 事务 1 在非聚集索引键上取得
U
锁
AccountId = 1000 然后在基础 table 行上获得 U
锁,转换
X
锁并释放非聚集索引上的 U
锁
钥匙。 X
锁一直保持到事务结束。
- 事务 2 在非聚集索引键上取得
U
锁
AccountId = 1000 但在尝试获取基础上的 U
锁时被阻止
table 行由事务 1 的 X
锁。
- 事务 1 运行它的第二个
UPDATE
并尝试获取 U
锁
AccountId = 1000 的非聚集索引键。这已经是
由事务 2 持有。死锁。
我们遇到了这样一种情况,当尝试在同一事务中两次从两个同时连接更新 table 时发生死锁,并且每次查询 运行 时都可重现 运行 on 2 query windows 在 SSMS 中。 (AccountId 列是非聚集键)
见下文。
在 AccountId 列上创建聚集键后,死锁不再发生。是什么导致了这种行为?
如果 AccountId 上没有聚集索引并且此列上没有聚集索引,SQL 服务器必须锁定索引键然后锁定行。
所以第一次更新将会成功,更新后您将只有一行锁定在 table。
第二次更新将尝试锁定此行,并将等待第一次更新释放锁。它将能够获得索引上的键锁。
第三次更新将尝试锁定索引键,并将等待第二次更新释放锁。死锁。
我能够使用以下方法重现它 table:
create table test5 (x int,y int)
insert into test5 values (10,15)
GO
insert into test5 values (11,15)
GO 10000
create index ix on test5(x)
select * from test5
begin transaction
update test5
set y = 5
where x = 10
-- wait here
update test5
set y = 5
where x = 10
rollback
执行计划有一个非聚集索引查找输出基本 table RID (Bmk1000
) 和一个使用 RID 查找的 UPDATE
运算符。这提供了死锁中涉及的两个资源(非聚集索引键和与 RID 对应的基本 table 行)。
- 事务 1 在非聚集索引键上取得
U
锁 AccountId = 1000 然后在基础 table 行上获得U
锁,转换X
锁并释放非聚集索引上的U
锁 钥匙。X
锁一直保持到事务结束。 - 事务 2 在非聚集索引键上取得
U
锁 AccountId = 1000 但在尝试获取基础上的U
锁时被阻止 table 行由事务 1 的X
锁。 - 事务 1 运行它的第二个
UPDATE
并尝试获取U
锁 AccountId = 1000 的非聚集索引键。这已经是 由事务 2 持有。死锁。