SQL Server 2005 deadlock with nonclustered index

10 sql-server deadlock sql-server-2005

Can anybody help me for a deadlock in SQL Server 2005?

For a simple test, I have a table "Book" which has a primary key (id), and a column name. The default index of this primary key is nonclustered.

当两个会话同时运行时发生死锁.活动监视器显示第一个会话"//步骤1"使用X锁定锁定行(摆脱锁定).第二个会话保持行U锁定和键U锁定.死锁图片显示第一个会话的"// step2"需要密钥U锁定.

如果索引是群集的,则在这种情况下没有死锁."// step 1"将同时保持行和键锁定,因此没有问题.我可以理解锁定一行也会锁定索引,因为聚簇索引的叶节点是行数据.

但是,为什么非聚集索引就是这样呢?如果第二个会话持有密钥U锁,为什么第一个会话的"步骤1"不保持此锁定,因为它们与更新语句相同.

--// first session
BEGIN TRAN
  update Book set name = name where id = 1 //step 1
  WaitFor Delay '00:00:20'
  update Book set name = 'trans' where id = 1 //step2
COMMIT

--// second session
BEGIN TRAN
--// this statement will keep both RID(U lock) and KEY(U lock) if first session did not use HOLDLOCK
  update Book set name = name where id = 1
COMMIT
Run Code Online (Sandbox Code Playgroud)

zin*_*lon 11

这里的相关因素是您在where子句中使用具有非聚簇索引的列.当SQL Server处理更新时,它会是这样的:

  1. 查找要更新的行,对触摸的数据执行U锁定
  2. 更新行,对修改后的数据进行X锁定

语句完成后(在默认READ COMMITTED隔离下),U锁被释放但X锁被保持到事务结束以保持隔离.

在非聚簇索引情况下,SQL Server在id上寻找索引并使用它来查找实际行.锁定如下:

  1. (会话1,步骤1)对id = 1的索引键值采用U锁定
  2. (会话1,步骤1)对ID为1的行的RID采用X锁定
  3. (第1节,第1步)U锁被释放
  4. (会话2)对id = 1的索引键值采用U锁定
  5. (会话2)对于id为1的行,RID被阻止了RID
  6. (会话1,步骤2)对于id = 1的索引键值,U锁被阻止 - DEADLOCK

但是,当索引是聚簇索引时,没有单独的步骤将索引键转换为行 - 聚簇索引值行标识符.因此,锁定最终会像这样:

  1. (会话1,步骤1)对id = 1的索引键值采用U锁定
  2. (会话1,步骤1)U锁升级为X锁
  3. (会话2)对于id = 1的索引键值,U锁被阻止
  4. (会话1,步骤2)锁定已经保持在id = 1的索引键值上
  5. (会话1,提交)锁定已发布
  6. (第2节)授予U锁定
  7. (会话2)U锁升级为X锁
  8. (第2节)锁定发布

与往常一样,请记住虽然这可能是在这种情况下使用的查询计划,但优化器可以自由地以不同的方式执行操作.例如,它可以选择表扫描或取出更粗粒度的锁.在这些情况下,可能不会发生僵局.