带有非聚簇索引的SQL Server 2005死锁

时间:2010-01-20 07:22:08

标签: sql-server sql-server-2005 deadlock

任何人都可以帮我解决SQL Server 2005中的死锁问题吗?

对于一个简单的测试,我有一个表“Book”,它有一个主键(id)和一个列名。此主键的默认索引为 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

3 个答案:

答案 0 :(得分:11)

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

  1. 查找要更新的行,对已触摸的数据执行U锁定
  2. 更新行,对修改后的数据进行X锁定
  3. 语句完成后(在默认的READ COMMITTED隔离下),U锁被释放,但X锁被保留到事务结束以保持隔离。

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

    1. (会话1,步骤1)对id = 1
    2. 的索引键值进行U锁定
    3. (会话1,步骤1)对ID为1 =
    4. 的行的RID采取X锁定
    5. (第1节,第1步)U锁已释放
    6. (会话2)对id = 1的索引键值采取U锁定
    7. (会话2)针对ID为1
    8. 的行的RID阻止X锁定
    9. (会话1,步骤2)对于id = 1的索引键值,U锁被阻止 - DEADLOCK
    10. 但是,当索引是聚簇索引时,没有单独的步骤将索引键转换为行 - 聚簇索引值行标识符。因此,锁定最终会像这样:

      1. (会话1,步骤1)对id = 1
      2. 的索引键值进行U锁定
      3. (会话1,步骤1)U锁升级为X锁
      4. (会话2)对于id = 1
      5. 的索引键值,U锁被阻止
      6. (会话1,步骤2)已锁定id = 1
      7. 的索引键值
      8. (会话1,提交)锁定已发布
      9. (第2节)授予U锁
      10. (会话2)U锁升级为X锁
      11. (会话2)锁定释放
      12. 与往常一样,请记住,尽管这可能是此情况下使用的查询计划,但优化程序可以自由地执行不同的操作。例如,它可以选择表扫描或取出更粗粒度的锁。在这些情况下,可能不会发生僵局。

答案 1 :(得分:0)

此链接提供了许多有用的建议:SQL Server deadlocks between select/update or multiple selects

以下几点可以帮助人们回答您的问题:

      
  1. 您使用的是什么事务隔离级别?
  2.   
  3. 是否允许锁定升级(例如,从行到页面)?
  4.   
  5. 'name'列上是否有索引?

答案 2 :(得分:0)

您的第一次更新实际上并未修改任何内容:

update Book set name = name where id = 1

您实际更改了列的命令,然后行上将保留一个独占锁。