SQL Server - SELECT 语句采用排它锁 - 为什么?

Fru*_*aun 1 sql-server deadlock locking blocking deadlock-graph

我试图理解为什么 SQL Server (2014) 在死锁场景中放置独占键锁。我已将整个死锁图粘贴在下面。

我很困惑,因为死锁发生在两个 SELECT 语句之间,两者都作为单个 READ COMMITTED 语句运行,而不是在事务内运行(因此同一事务中的其他地方没有发生更新等)。

我相信发生死锁是因为每个进程都在索引上创建一系列键锁,并且由于获取它们的顺序,所以发生了死锁。但是,如果进程仅创建共享锁,则不应出现死锁(根据我的理解)!

所以根本问题是 - 为什么这些 SELECT 语句会获取独占键锁?

我希望这只是我对锁定的误解。任何建议将不胜感激。

    <deadlock>
  <victim-list>
    <victimProcess id="processd7f647848" />
  </victim-list>
  <process-list>
    <process id="processd7f647848" taskpriority="0" logused="25948" waitresource="KEY: 32:72057594051428352 (e2d15ad895e9)" waittime="5014" ownerId="394724294049" transactionname="user_transaction" lasttranstarted="2022-04-06T09:46:18.770" XDES="0x67bf873350" lockMode="S" schedulerid="15" kpid="26196" status="suspended" spid="306" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2022-04-06T09:46:18.783" lastbatchcompleted="2022-04-06T09:46:18.783" lastattention="1900-01-01T00:00:00.783" clientapp=".Net SqlClient Data Provider" hostname="myhost" hostpid="1500" loginname="myuser" isolationlevel="read committed (2)" xactid="394724294049" currentdb="32" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="MyDatabase.Main.GetMessages" line="154" stmtstart="11934" stmtend="12156" sqlhandle="0x03002000ebd98264d1bfae0066ae000001000000000000000000000000000000000000000000000000000000">
SELECT ID, Message FROM [dbo].[Messages] WHERE MessageType = @Type    </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 32 Object Id = 1686297067]   </inputbuf>
    </process>
    <process id="processd7f61f848" taskpriority="0" logused="27364" waitresource="KEY: 32:72057594051428352 (1b9d314f70e5)" waittime="6194" ownerId="394724293688" transactionname="user_transaction" lasttranstarted="2022-04-06T09:46:18.763" XDES="0x12ca804d350" lockMode="S" schedulerid="10" kpid="27008" status="suspended" spid="487" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2022-04-06T09:46:18.780" lastbatchcompleted="2022-04-06T09:46:18.780" lastattention="1900-01-01T00:00:00.780" clientapp=".Net SqlClient Data Provider" hostname="myhost" hostpid="1500" loginname="myuser" isolationlevel="read committed (2)" xactid="394724293688" currentdb="32" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
      <executionStack>
        <frame procname="MyDatabase.Main.GetMessages" line="154" stmtstart="11934" stmtend="12156" sqlhandle="0x03002000ebd98264d1bfae0066ae000001000000000000000000000000000000000000000000000000000000">
SELECT ID, Message FROM [dbo].[Messages] WHERE MessageType = @Type   </frame>
      </executionStack>
      <inputbuf>
Proc [Database Id = 32 Object Id = 1686297067]   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <keylock hobtid="72057594051428352" dbid="32" objectname="MyDatabase.dbo.Messages" indexname="IX_Messages_Type" id="lockba5d041f80" mode="X" associatedObjectId="72057594051428352">
      <owner-list>
        <owner id="processd7f61f848" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="processd7f647848" mode="S" requestType="wait" />
      </waiter-list>
    </keylock>
    <keylock hobtid="72057594051428352" dbid="32" objectname="MyDatabase.dbo.Messages" indexname="IX_Messages_Type" id="lock11fad883600" mode="X" associatedObjectId="72057594051428352">
      <owner-list>
        <owner id="processd7f647848" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="processd7f61f848" mode="S" requestType="wait" />
      </waiter-list>
    </keylock>
  </resource-list>
</deadlock>
Run Code Online (Sandbox Code Playgroud)

Eri*_*ing 5

你应该做的第一件事是运行这个:

SELECT sp = OBJECT_NAME(1686297067, 32);
Run Code Online (Sandbox Code Playgroud)

这将为您提供属于死锁一部分的存储过程的名称,而 SQL Server 不会费心去自行找出该名称。

这应该会让您看到与您正在查看的死锁图更密切相关的语句。

我之前在这里讨论过这个问题:Deadlock Graph Frustrations,其中看似不相关的语句位于死锁 XML 中。