4

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

我期望的是insert对聚集索引进行排他锁,而select非聚集索引进行共享锁定,然后它们彼此等待释放各自的索引。

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

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

Edit2:这是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 隔离引起的范围扫描(谷歌搜索)。但是,我仍然不明白这是如何发生的,以及推荐的补救措施是什么。

4

1 回答 1

6

考虑从访问同一记录的两个并行事务(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因此它等待。

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

  1. 使用已提交的读取作为整体隔离级别并且不可序列化。这将防止获取范围锁。
  2. 使用更新锁(UPDLOCK)读取。这将排在第一位。因此,其他事务将在 Read 本身等待。
  3. 避免读取和插入/更新模式。直接继续插入/更新,让它失败。

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

于 2012-09-26T07:31:03.497 回答