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 持有。死锁。