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 对 聚集索引 采取排他锁,而 select 对 non -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 中的死锁图显示同一集群键上的互锁的主要内容,如果未能解决你的问题,请参考以下文章