3

我正在解决我在生产环境中看到的一些死锁问题,我对此很陌生,但对我来说有些奇怪。所以我有下面的死锁图:

死锁图

死锁的右侧是以下更新:

UPDATE order_sub_line SET sub_line_status = 300 WHERE order_sub_line_id = '75C387EC-A1A7-4587-9FA0-DD33A49009BC'

在我看来,此更新正在尝试获取 2 个页面锁定。order_sub_line_id 是聚集索引。

这是否应该尝试获取 2 个页面锁,如果是,为什么?

附加信息:

死锁牺牲品是一个视图(加入了一些其他表,包括 order_sub_line),它本质上是在该表上运行以下查询:

select top(50) * from order_sub_line osl where osl.sub_line_type = 1 and osl.sub_line_status < 375

除了 order_sub_line.order_sub_line_id 上的聚集主键索引外, order_sub_line 上没有索引

执行计划: 在此处输入图像描述

死锁xml:

<deadlock-list>
 <deadlock victim="process4224eccf8">
  <process-list>
   <process id="process4224eccf8" taskpriority="0" logused="0" waitresource="PAGE: 7:1:13448 " waittime="1628" ownerId="1683307923" transactionname="SELECT" lasttranstarted="2013-07-31T08:45:53.157" XDES="0x48afafc40" lockMode="S" schedulerid="2" kpid="1208" status="suspended" spid="151" sbid="0" ecid="15" priority="0" trancount="0" lastbatchstarted="2013-07-31T08:45:53.157" lastbatchcompleted="2013-07-31T08:45:53.157" lastattention="1900-01-01T00:00:00.157" clientapp="ExactaAOR" hostname="BASTIAN-PC" hostpid="7336" isolationlevel="read committed (2)" xactid="1683307923" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="72" sqlhandle="0x0200000055b04f0c4d136173c4d51458bdb5002bfe5801370000000000000000000000000000000000000000">
SELECT TOP (@p0)  this_.TRANSPORT_CNTNR_ID as TRANSPORT1_9_0_, this_.CNTNR_NAME as CNTNR2_9_0_, this_.CNTNR_TYPE as CNTNR3_9_0_, this_.CNTNR_HEIGHT as CNTNR4_9_0_, this_.CNTNR_WIDTH as CNTNR5_9_0_, this_.CNTNR_DEPTH as CNTNR6_9_0_, this_.CNTNR_WEIGHT as CNTNR7_9_0_, this_.PARENT_CNTNR_ID as PARENT8_9_0_, this_.RESERVATION_LOC_ID as RESERVAT9_9_0_, this_.WORK_AREA_ID as WORK10_9_0_, this_.WORK_AREA_NAME as WORK11_9_0_, this_.GROUP_ID as GROUP12_9_0_, this_.RELEASE_STATUS as RELEASE13_9_0_, this_.RELEASE_TIME as RELEASE14_9_0_, this_.PRINT_STATUS as PRINT15_9_0_, this_.SUB_LINE_COUNT as SUB16_9_0_, this_.ORDER_ID as ORDER17_9_0_, this_.QTY_REQUESTED as QTY18_9_0_, this_.ORDER_NAME as ORDER19_9_0_, this_.ORDER_PRIORITY as ORDER20_9_0_, this_.ORDER_STATUS as ORDER21_9_0_, this_.ON_HOLD as ON22_9_0_, this_.DUE_DATE as DUE23_9_0_, this_.ORDER_SUB_LINE_STATUS as ORDER24_9_0_ FROM V_CARTON_RELEASE this_ WHERE (this_.RELEASE_STATUS = @p1 and this_.ORDER_SUB_LINE_STATUS &lt; @p2) ORDER BY this_.RELEASE_TIME asc     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@p0 int,@p1 nvarchar(4000),@p2 int)SELECT TOP (@p0)  this_.TRANSPORT_CNTNR_ID as TRANSPORT1_9_0_, this_.CNTNR_NAME as CNTNR2_9_0_, this_.CNTNR_TYPE as CNTNR3_9_0_, this_.CNTNR_HEIGHT as CNTNR4_9_0_, this_.CNTNR_WIDTH as CNTNR5_9_0_, this_.CNTNR_DEPTH as CNTNR6_9_0_, this_.CNTNR_WEIGHT as CNTNR7_9_0_, this_.PARENT_CNTNR_ID as PARENT8_9_0_, this_.RESERVATION_LOC_ID as RESERVAT9_9_0_, this_.WORK_AREA_ID as WORK10_9_0_, this_.WORK_AREA_NAME as WORK11_9_0_, this_.GROUP_ID as GROUP12_9_0_, this_.RELEASE_STATUS as RELEASE13_9_0_, this_.RELEASE_TIME as RELEASE14_9_0_, this_.PRINT_STATUS as PRINT15_9_0_, this_.SUB_LINE_COUNT as SUB16_9_0_, this_.ORDER_ID as ORDER17_9_0_, this_.QTY_REQUESTED as QTY18_9_0_, this_.ORDER_NAME as ORDER19_9_0_, this_.ORDER_PRIORITY as ORDER20_9_0_, this_.ORDER_STATUS as ORDER21_9_0_, this_.ON_HOLD as ON22_9_0_, this_.DUE_DATE as DUE23_9_0_, this_.ORDER_SUB_LINE_STATUS as ORDER24_9_0_ FROM V_CARTON_RELEASE this_ WHERE (this_.RELEASE_STATUS = @p1 and this_.ORDER_SUB_LINE_STATUS &lt; @p2) ORDER    </inputbuf>
   </process>
   <process id="process4bf7bdc38" taskpriority="0" logused="8608" waitresource="PAGE: 7:1:13447 " waittime="1616" ownerId="1683308190" transactionname="user_transaction" lasttranstarted="2013-07-31T08:45:53.450" XDES="0x4ebd456a8" lockMode="IX" schedulerid="1" kpid="6032" status="suspended" spid="85" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2013-07-31T08:45:53.450" lastbatchcompleted="2013-07-31T08:45:53.450" lastattention="1900-01-01T00:00:00.450" clientapp="ExactaAOR" hostname="BASTIAN-PC" hostpid="7336" loginname="asapdb" isolationlevel="read committed (2)" xactid="1683308190" currentdb="7" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="60" sqlhandle="0x02000000109639184c42e35fa55701e017640d83bd4818c30000000000000000000000000000000000000000">
UPDATE ORDER_SUB_LINE SET SUB_LINE_STATUS = @p0 WHERE ORDER_SUB_LINE_ID = @p1     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@p0 int,@p1 uniqueidentifier)UPDATE ORDER_SUB_LINE SET SUB_LINE_STATUS = @p0 WHERE ORDER_SUB_LINE_ID = @p1    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <pagelock fileid="1" pageid="13448" dbid="7" subresource="FULL" objectname="Exactadb.dbo.order_sub_line" id="lock4cf017000" mode="IX" associatedObjectId="72057594460962816">
    <owner-list>
     <owner id="process4bf7bdc38" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process4224eccf8" mode="S" requestType="wait"/>
    </waiter-list>
   </pagelock>
   <pagelock fileid="1" pageid="13447" dbid="7" subresource="FULL" objectname="Exactadb.dbo.order_sub_line" id="lock4a4554500" mode="S" associatedObjectId="72057594460962816">
    <owner-list>
     <owner id="process4224eccf8" mode="S"/>
    </owner-list>
    <waiter-list>
     <waiter id="process4bf7bdc38" mode="IX" requestType="wait"/>
    </waiter-list>
   </pagelock>
  </resource-list>
 </deadlock>
</deadlock-list>
4

1 回答 1

2

根据所提供的数据作出以下推论,

  1. 两个事务都在读提交隔离级别下运行。
  2. 一项事务正在执行多个单独的行更新。从一个进程持有 IX 锁并等待另一个进程这一事实可以看出这一点。根据执行计划,UPDATE 语句使用单行聚集索引查找。因此它将在 KEY 级别获取 X 锁和在页面级别获取 IX 锁。
  3. SELECT 语句正在获取 PAGE 级别粒度的锁。阅读页面后,SELECT 也会保留锁。在 READ COMMITTED ISOLATION LEVEL 的正常情况下,一条 SELECT 语句会在读取后立即获取和释放 SHARED 锁。

有了这些发现,我几乎可以肯定,死锁的发生是由于涉及称为 UNORDERED PREFETCH 的查询优化的特殊情况。这是我知道的唯一一种情况,在 READ COMMITTED 隔离级别下运行的 SELECT 语句会保留 SHARED 锁,直到语句结束。

可以在https://web.archive.org/web/20120806214319/http://sqlindian.com/2012/07/13/deadlock-on-select-due找到此类死锁的重现和可能的解决方案 -to-unordered-prefetch/

于 2013-08-16T21:34:15.277 回答