SQL Server Profiler 中的死锁图显示同一集群键上的互锁

Posted

技术标签:

【中文标题】SQL Server Profiler 中的死锁图显示同一集群键上的互锁【英文标题】:Deadlock graph in SQL server profiler shows mutual lock on the same clustered key 【发布时间】:2012-09-25 08:19:34 【问题描述】:

我正在尝试使用 SQL Server Profiler 确定死锁的原因。 这是死锁图: 两个语句都是插入,后跟select scope_identity(); 实际上,有 2 个并发进程在一个循环中重复执行 insert-select_identity

我希望 insert聚集索引 采取排他锁,而 selectnon -clustered index,然后他们互相等待释放各自的indeces。

我看到的是两个进程都在等待相同的资源被释放——聚集索引。怎么会这样?特定的追索权应该属于一个过程或另一个过程。我在这里想念什么? 提前感谢大家。

已编辑:是的,隔离级别是可序列化的。 PS:可能我对非聚集索引上的共享锁的假设是错误的,因为我的选择不包含where 语句

编辑2: 这是xml的一部分:

 <resource-list>
   <keylock hobtid="72057594148028416" dbid="29" objectname="<confidential>" indexname="PK_WP_Inbound_StockTransactionLine" id="lock9641700" mode="RangeS-S" associatedObjectId="72057594148028416">
    <owner-list>
     <owner id="process8e09288" mode="RangeS-S"/>
    </owner-list>
    <waiter-list>
     <waiter id="process991ce08" mode="RangeI-N" requestType="convert"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057594148028416" dbid="29" objectname="<confidential>" indexname="PK_WP_Inbound_StockTransactionLine" id="lock9641700" mode="RangeS-S" associatedObjectId="72057594148028416">
    <owner-list>
     <owner id="process991ce08" mode="RangeS-S"/>
    </owner-list>
    <waiter-list>
     <waiter id="process8e09288" mode="RangeI-N" requestType="convert"/>
    </waiter-list>
   </keylock>
  </resource-list>

据此,我认为这是由 SERIALIZABLE 隔离引起的范围扫描(谷歌搜索)。但是,我仍然不明白这是怎么发生的以及推荐的补救措施是什么。

【问题讨论】:

您使用的是什么隔离级别?可序列化?如果是这样,有什么理由吗? 是的,我忘了说,对不起。我已经编辑了帖子 发布死锁 XML,而不是图像。图片不完整、具有误导性且经常出错。 您肯定在处理聚集索引上的不同行。发布完整的 XML。 【参考方案1】:

考虑从访问同一记录的两个并行事务(T1 和 T2)调用以下代码。

Read LastRow
Insert AtLastRow

假设上下文切换发生在Read。所以操作顺序是

T1 Read LastRow
T2 Read LastRow
T2 Insert AtLastRow // This will wait for T1 to finish.
T1 Insert AtLastRow // This will wait for T2 to finish. Hence deadlock!

以上读取将采用Range S-S 锁定。最后插入还需要Range I-N,它与其他事务持有的现有Range S-S 锁不兼容。因此它等待。

可以有多种方法来解决这个问题。

    使用已提交读取作为整体隔离级别且不可序列化。 这将防止获取范围锁。 使用更新锁 (UPDLOCK) 读取。这将采取独家更新 锁定第一名。因此,其他事务将在 Read 本身等待。 避免读取和插入/更新模式。直接往前走 使用插入/更新并让它失败。

如果您有任何问题,请告诉我。

【讨论】:

3.避免读取和插入/更新模式。那我该如何处理失败呢? IE。如果记录已经存在,我需要做其他事情(例如,显示特定消息)。我应该只是 w8 例外吗?然后什么?解析异常数据以确定其确切原因?此外,为什么插入/更新会失败?考虑我需要检查业务逻辑约束而不是数据模型约束的情况。可能,解决方案也是将读取操作移出事务。但在我们的例子中,这是 2much 重构,所以我将更改隔离 lvl 作为解决方法。谢谢=) @ArturUdod 假设您读取了一行 key = 10 并递增值。您可以读取该值然后更新它,也可以继续更新。如果密钥不存在,上述更新将失败。现在,如果您检测到失败,则插入值为 1 的键 10。这有帮助吗? 是的,我明白了。但是,正如我所说:让我们假设我只想增加一个小于 100 的值。如果它已经是 100,那么我想显示一条消息。在课程之外,可以有一个“检查”约束,但这是一个简单的例子。我可能有一个更复杂的验证业务逻辑,我真的需要在修改数据之前执行读取。 @ArturUdod 在这种情况下,选择第二个选项。 "读取更新锁 (UPDLOCK)。" 实际上,在我的情况下,如果仅针对这种特殊情况将隔离级别切换为已提交读,则不会有任何危险。幻读不太可能发生(只是因为服务器逻辑阻止了这种情况)。感谢您的回复。

以上是关于SQL Server Profiler 中的死锁图显示同一集群键上的互锁的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server Profiler的简单使用

SQL Server Profiler的简单使用

sql server profiler 是啥文件

SQL Server 收集数据库死锁信息

(4.7)怎么捕获和记录SQL Server中发生的死锁?

SQL Server死锁图:请解释一下