当 where 子句列中没有聚集索引时,并行更新导致死锁
Posted
技术标签:
【中文标题】当 where 子句列中没有聚集索引时,并行更新导致死锁【英文标题】:Parallel updates causing deadlock when no clustered index in where clause column 【发布时间】:2020-05-23 01:46:43 【问题描述】:我们遇到了这样一种情况,即在同一事务中尝试从两个同时连接更新表两次时发生死锁,并且每次在 SSMS 中的 2 个查询窗口上运行查询时都可以重现死锁。 (AccountId 列是非聚集键)
见下文。
在 AccountId 列上创建集群键后,不再发生死锁。是什么导致了这种行为?
【问题讨论】:
表格有多少行? 大约 50,000 条记录 大概只有一行AccountId = 1000?
sorry..我误会了你的问题,删除了我的回复,正好有一行,但是一行有35列,这就是我的意思。
【参考方案1】:
如果没有 AccountId 上的聚集索引和此列上的非聚集索引,SQL Server 必须先锁定索引键,然后再锁定行。
因此第一次更新将成功,更新后您将只有一行锁定在表中。 第二次更新将尝试锁定此行,并将等待第一次更新的锁定释放。它将能够获得索引上的键锁。 第三次更新将尝试锁定索引键,并等待第二次更新释放锁。死锁。
我能够使用下表重现它:
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
【讨论】:
我相信表中需要有一定数量的行才能升级为表锁?我无法复制该问题,但我的 Accounts 表中只有一行,因此更新只需要行锁定 仍然无法复制 200,000 行:s 对不起@Piotr,我仍然无法理解你的解释。为什么第一次更新后该行仍会被锁定?基本上更新已经完成,所以锁可以被释放吧? 对所有更新行的锁定始终保持到事务结束。 有道理。【参考方案2】:执行计划具有输出基表 RID (Bmk1000
) 的非聚集索引查找和使用 RID 查找的 UPDATE
运算符。这就提供了死锁所涉及的两种资源(非聚集索引键和RID对应的基表行)。
U
锁定
AccountId = 1000 然后在基表行上获得U
锁,转换
到X
锁定并释放非聚集索引上的U
锁定
钥匙。 X
锁一直保持到事务结束。
事务 2 在非聚集索引键上使用 U
锁定
AccountId = 1000,但在尝试获取基础上的 U
锁定时被阻止
表行按事务 1 的 X
锁。
事务 1 运行它的第二个 UPDATE
并尝试获取 U
锁定
AccountId = 1000 的非聚集索引键。这已经是
由事务 2 持有。死锁。
【讨论】:
以上是关于当 where 子句列中没有聚集索引时,并行更新导致死锁的主要内容,如果未能解决你的问题,请参考以下文章
SQL中的WHERE子句中为啥不允许应用聚集函数呢?请通俗的解释一下或者谈谈自己的见解!