【问题标题】:Why is this SQL Server query deadlocking?为什么这个 SQL Server 查询会死锁?
【发布时间】:2011-09-03 06:07:30
【问题描述】:

谁能告诉我为什么下面的 SQL Server 查询会死锁以及修复它的解决方案是什么?

<deadlock-list>
  <deadlock victim="process88b5b8">
    <process-list>
      <process id="process88b5b8" taskpriority="0" logused="76132" waitresource="RID: 32:1:151867:174" waittime="5093" ownerId="65554098" transactionguid="0xedf3314c05f1124cbe8d480cd092e03e" transactionname="DTCXact" lasttranstarted="2011-09-02T19:00:29.690" XDES="0x1029e040" lockMode="S" schedulerid="1" kpid="5108" status="suspended" spid="118" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2011-09-02T19:00:31.317" lastbatchcompleted="2011-09-02T19:00:31.300" hostname="MELWFPL382S" hostpid="0" loginname="MM4" isolationlevel="repeatable read (3)" xactid="65554098" currentdb="32" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="adhoc" line="1" stmtstart="78" sqlhandle="0x020000004b4b0a0d63e1040095143cbaa0174ffc3e076067"> delete from PARTIES where PARTYEXTERNALREF=@P0 and ISCOUNTERPARTY='N' and PARTYID in (select PARTYID from NAB_PARTY_EXTEND (nolock) where PARTYTYPE=@P1)     </frame>
          <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">unknown     </frame>
        </executionStack>
        <inputbuf>(@P0 nvarchar(4000),@P1 nvarchar(4000))delete from PARTIES where PARTYEXTERNALREF=@P0 and ISCOUNTERPARTY='N' and PARTYID in (select PARTYID from NAB_PARTY_EXTEND (nolock) where PARTYTYPE=@P1)                    </inputbuf>
      </process>
      <process id="process9196a8" taskpriority="0" logused="132612" waitresource="RID: 32:1:140302:31" waittime="5046" ownerId="65554657" transactionguid="0x7313c78fecc8914dac3ed821cd7c21fe" transactionname="DTCXact" lasttranstarted="2011-09-02T19:00:34.100" XDES="0x12835778" lockMode="S" schedulerid="2" kpid="3692" status="suspended" spid="94" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2011-09-02T19:00:35.690" lastbatchcompleted="2011-09-02T19:00:35.687" hostname="MELWFPL382S" hostpid="0" loginname="MM4" isolationlevel="repeatable read (3)" xactid="65554657" currentdb="32" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="adhoc" line="1" stmtstart="78" sqlhandle="0x020000004b4b0a0d63e1040095143cbaa0174ffc3e076067">delete from PARTIES where PARTYEXTERNALREF=@P0 and ISCOUNTERPARTY='N' and PARTYID in (select PARTYID from NAB_PARTY_EXTEND (nolock) where PARTYTYPE=@P1)     </frame>
          <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">unknown     </frame>
        </executionStack>
        <inputbuf>(@P0 nvarchar(4000),@P1 nvarchar(4000))delete from PARTIES where PARTYEXTERNALREF=@P0 and ISCOUNTERPARTY='N' and PARTYID in (select PARTYID from NAB_PARTY_EXTEND (nolock) where PARTYTYPE=@P1)                    </inputbuf>
      </process>
    </process-list>
    <resource-list>
      <ridlock fileid="1" pageid="140302" dbid="32" objectname="mm4_melwfpl382s.dbo.COUNTERPARTYSSI" id="lock170fa500" mode="X" associatedObjectId="72057595803336704">
        <owner-list>
          <owner id="process88b5b8" mode="X" />
        </owner-list>
        <waiter-list>
          <waiter id="process9196a8" mode="S" requestType="wait" />
        </waiter-list>
      </ridlock>
      <ridlock fileid="1" pageid="151867" dbid="32" objectname="mm4_melwfpl382s.dbo.COUNTERPARTYSSI" id="lock20e65d80" mode="X" associatedObjectId="72057595803336704">
        <owner-list>
          <owner id="process9196a8" mode="X" />
        </owner-list>
        <waiter-list>
          <waiter id="process88b5b8" mode="S" requestType="wait" />
        </waiter-list>
      </ridlock>
    </resource-list>
  </deadlock>
</deadlock-list>

我不明白两个进程如何在同一个对象上拥有独占锁。

在 PARTIES 表上有一个索引 (IDX_NC_PARTIES_PARTYEXTERNALREF_ISCOUNTERPARTY_PARTYID),并且数据库设置为读取提交的快照。

谢谢,

韦恩。

【问题讨论】:

  • 您的触发器可能是激活和锁定资源。如果您有触发器,请先尝试禁用它们并查看您的查询是否运行。
  • 你的删除查询有问题吗?
  • 我会在星期一检查是否涉及任何触发器。不幸的是,这不是那么容易重现的问题,即我需要将数千条消息输入我们的应用程序并等待数小时才能发生死锁。死锁是随机发生的,即它看起来像是各个进程之间的时间问题。添加上面提到的索引是我摆脱死锁的最新尝试。

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


【解决方案1】:
  • 进程 9196a8 在 X 模式下拥有页面 151867 插槽 174,并希望在 S 模式下拥有页面 140302 插槽 31
  • 进程 88b5b8 在 X 模式下拥有页面 140302 插槽 31,并且在 S 模式下需要页面 151867 插槽 174
  • 两个删除在isolationlevel="repeatable read (3)" 下运行

所以死锁发生在表的基堆上(RID 锁而不是键锁意味着堆不是 Btree)。高隔离级别(可能由 DTC 引起,从确切名称来看)使 RCSI 设置无关紧要。

PARTYEXTERNALREF 和 PARTYTYPE 列是什么类型?传入的参数是 NVARCHAR(即 Unicode),如果列是 VARCHAR(即 Ascii),那么由于 data type precedence 的规则,将不会使用 NC 索引。由于涉及到表扫描,再加上使用的隔离级别很高,死锁几乎是不可避免的。

解决方案是对@P0 和@P1 使用 VARCHAR 类型参数,以便利用 NC 索引来避免表扫描。

如果参数已经是 VARCHAR 类型,并且您可以从执行计划中确认使用了 NC 上的搜索,那么我的第一个问题将是 else 事务在做什么,除了删除语句?

顺便说一句,您只提供了 NC 索引的名称,但我假设它位于 (PARTYEXTERNALREF, ISCOUNTERPARTY, PARTYID)

更新

由于您的评论说列 NVARCHAR,因此表扫描假设可能是错误的。还有另外三种可能导致死锁需要调查:

  • 事务在 DELETE 之前运行的任何其他语句(这是最有可能的)
  • 死锁中涉及的两个 DELETE 语句选择的行中的任何重叠
  • hash collision

对于前两个假设,您现在可以做任何事情(调查它们是否正确)。对于最后一个我可以告诉你如何验证它,但不是微不足道的。这不太可能发生并且有点难以证明,但它是可能的。既然您知道死锁案例(附加的 XML),请将其用作调查基础:

  • 恢复数据库with stop at 2011-09-02T19:00:29.690的时间点副本
  • 运行DBCC TRACEON(3604,-1)
  • 使用DBCC PAGE (&lt;restored db id&gt;, 1, 151867, 3)检查槽174中​​的值
  • 使用 DBCC PAGE(, 1, 140302, 3)` 检查插槽 31 处的值
  • 运行SELECT %%lockres%% FROM PARTIES WHERE PARTYEXTERNALREF = ... AND ISCOUNTERPARTY='N' and PARTYID=... 并传入上面读取的值
  • 比较生成的锁哈希值,如果它们匹配,则您有哈希冲突,这会导致死锁。

【讨论】:

  • 前两列是 nvarchar,第三列是 int。您在最后一个假设中是正确的。顺便说一句,我不是 DBA,所以我需要更多时间来消化上述信息。据我所知,只涉及删除和选择语句。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-27
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多