【问题标题】:SQL Deadlock with keylock (UPDLOCK, ROWLOCK, HOLDLOCK) on same object同一个对象上带有键锁(UPDLOCK、ROWLOCK、HOLDLOCK)的 SQL 死锁
【发布时间】:2021-04-23 16:58:30
【问题描述】:

我试图了解由 2 个存储过程导致的死锁,它们试图查询/更新 Accounts 表中的相同对象。通常,我通过涉及的 2 个不同进程在 2 个不同的关联对象上看到 2 个键锁。但是在这种情况下,两个键锁都指向相同的关联对象,这令人困惑。请问我可以得到一些帮助来理解这一点吗?

注意:存储过程在具有可序列化隔离级别的事务中执行。

<deadlock>
  <victim-list>
    <victimProcess id="process_victim" />
  </victim-list>
  <process-list>
    <process id="process_victim" taskpriority="0" logused="6132" waitresource="KEY: 39:72057594582597632 (781c957ed006)" waittime="3279" ownerId="9627380307" transactionname="user_transaction" lasttranstarted="2021-01-14T15:47:08.737" XDES="0x30091b48428" lockMode="U" schedulerid="16" kpid="224656" status="suspended" spid="665" sbid="2" ecid="0" priority="0" trancount="1" lastbatchstarted="2021-01-14T15:47:08.770" lastbatchcompleted="2021-01-14T15:47:08.753" lastattention="1900-01-01T00:00:00.753" clientapp="accounts" hostname="2000000" hostpid="13528" loginname="dbUser" isolationlevel="serializable (4)" xactid="9627380307" currentdb="39" currentdbname="Trans" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="5716a4fb-ea5f-48ff-bc69-a20036b656f3.dbo.uspInsertMasterAccountTrans" queryhash="0x16139b535590589a" queryplanhash="0x30448d54aa8b7e4c" line="39" stmtstart="2412" stmtend="2708" sqlhandle="0x030027001088596482b8e700aeac000001000000000000000000000000000000000000000000000000000000">
SELECT  @currentAccountBalance = [ACC].Balance
    FROM [dbo].[Accounts] [ACC]
    WITH (UPDLOCK, ROWLOCK, HOLDLOCK)
    WHERE [ACC].AccountId = @accountId    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 39 Object Id = 1683589136]   </inputbuf>
    </process>
    <process id="process_other" taskpriority="0" logused="9536" waitresource="KEY: 39:72057594582597632 (ef38eb644a4b)" waittime="3264" ownerId="9627380654" transactionname="user_transaction" lasttranstarted="2021-01-14T15:47:08.757" XDES="0x30089c80428" lockMode="X" schedulerid="15" kpid="216124" status="suspended" spid="532" sbid="2" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-01-14T15:47:08.783" lastbatchcompleted="2021-01-14T15:47:08.780" lastattention="1900-01-01T00:00:00.780" clientapp="accounts" hostname="1000001" hostpid="24452" loginname="dbUser" isolationlevel="serializable (4)" xactid="9627380654" currentdb="39" currentdbname="Trans" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="5716a4fb-ea5f-48ff-bc69-a20036b656f3.dbo.uspInsertDebitCashTrans" queryhash="0xe4a5aeeea80cb67b" queryplanhash="0xcbcba51d221c9bc6" line="130" stmtstart="8450" stmtend="8956" sqlhandle="0x03002700bbf435679fb8e700aeac000001000000000000000000000000000000000000000000000000000000">
UPDATE [dbo].[Accounts]
    WITH (UPDLOCK, ROWLOCK, HOLDLOCK)
    SET Balance = Balance - @totalPaymentAmount, TimestampModified = GETUTCDATE()
    WHERE [ACC].AccountId = @debtorAccountId    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 39 Object Id = 1731589307]   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <keylock hobtid="72057594582597632" dbid="39" objectname="5716a4fb-ea5f-48ff-bc69-a20036b656f3.dbo.Accounts" indexname="PK_dbo.Accounts" id="lock28450c72100" mode="X" associatedObjectId="72057594582597632">
      <owner-list>
        <owner id="process_other" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process_victim" mode="U" requestType="wait" />
      </waiter-list>
    </keylock>
    <keylock hobtid="72057594582597632" dbid="39" objectname="5716a4fb-ea5f-48ff-bc69-a20036b656f3.dbo.Accounts" indexname="PK_dbo.Accounts" id="lock28ea0da7d00" mode="X" associatedObjectId="72057594582597632">
      <owner-list>
        <owner id="process_victim" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process_other" mode="X" requestType="wait" />
      </waiter-list>
    </keylock>
  </resource-list>
</deadlock>

【问题讨论】:

  • 不要“缩写”给我们完整的代码,否则无法诊断死锁。您的 SELECT 不知何故占用了 X 锁,所以在事务的后面一定还有一个 UPDATE
  • 您是否有索引以便通过搜索获取​​相关记录?你的where 子句是什么样的?当您从 update 查询中删除提示(无论如何都不需要)时会发生什么?
  • @Charlieface 添加了完整的查询,实际上它并不复杂。是的,我们需要 X 锁以便稍后在同一个存储过程中进行更新,并避免任何其他并行执行的更新。
  • 显示存储过程的完整代码。还为您的表和索引提供 DDL
  • @GSerg 是的,正确的索引已经到位。请注意,这是涉及 2 个不同的存储过程。我们需要更新查询中的提示,它位于 uspInsertDebitCashTrans 中,因为其中没有 SELECT before update 语句。

标签: sql sql-server deadlock database-deadlocks


【解决方案1】:

您使用过多且繁重的显式锁来同时执行这两个事务。

注销查询的“WITH (UPDLOCK, ROWLOCK, HOLDLOCK)”,就不会有这样的死锁了。

当然,我们有兴趣让交易中涉及的完整代码为您提供更多帮助...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多