SQL Server 2016 如何在两个不同的表上使用页锁死锁?

Jus*_*Deq 5 sql-server deadlock sql-server-2016

我正在使用 SQL Server 2016 并遇到一种情况,即通过页锁在两个不同的表上发生死锁。据我了解,数据库从不跨表共享页面,那么从表栏中选择怎么可能阻止表foo上的更新?特别是考虑到foo没有传入或传出的 FK(它是一个独立的、隔离的表)。

这是死锁xml:

<deadlock>
<victim-list>
    <victimProcess id="process200e2c2cca8"/>
</victim-list>
<process-list>
    <process id="process200e2c2cca8" taskpriority="0" logused="528" waitresource="PAGE: 7:1:463762 " waittime="2813" ownerId="232195085" transactionname="implicit_transaction" lasttranstarted="2019-06-18T01:57:05.067" XDES="0x201379a6430" lockMode="IX" schedulerid="2" kpid="2780" status="suspended" spid="77" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2019-06-18T01:57:05.267" lastbatchcompleted="2019-06-18T01:57:05.267" lastattention="1900-01-01T00:00:00.267" clientapp="Microsoft JDBC Driver for SQL Server" hostname="EC2AMAZ-U81HN6O" hostpid="0" loginname="mydb" isolationlevel="read committed (2)" xactid="232195085" currentdb="7" currentdbname="mydb" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
        <executionStack>
            <frame procname="adhoc" line="1" stmtstart="684" stmtend="1618" sqlhandle="0x0200000068ff5415175911deba600fac1e2197ddfe8b65890000000000000000000000000000000000000000">  unknown    </frame>
            <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">  unknown    </frame>
        </executionStack>
        <inputbuf>  update foo set version=@P0, field1=@P1, ... fieldN=@PN where id=@P25 and version=@P26                                                                                                                                                                                                          </inputbuf>
    </process>
    <process id="process2013de9b848" taskpriority="0" logused="1252" waitresource="PAGE: 7:1:481017 " waittime="2786" ownerId="232194529" transactionname="implicit_transaction" lasttranstarted="2019-06-18T01:57:03.300" XDES="0x2012a5ea430" lockMode="S" schedulerid="1" kpid="2884" status="suspended" spid="93" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2019-06-18T01:57:05.280" lastbatchcompleted="2019-06-18T01:57:05.280" lastattention="1900-01-01T00:00:00.280" clientapp="Microsoft JDBC Driver for SQL Server" hostname="EC2AMAZ-U81HN6O" hostpid="0" loginname="mydb" isolationlevel="read committed (2)" xactid="232194529" currentdb="7" currentdbname="mydb" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
        <executionStack>
            <frame procname="adhoc" line="1" stmtstart="132" stmtend="460" sqlhandle="0x02000000b1cb870e1f69a812df19c828461940c4f02bf9230000000000000000000000000000000000000000">  unknown    </frame>
            <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">  unknown    </frame>
        </executionStack>
        <inputbuf>  select count(*) as ct from bar where proj_id=@P0 and biz=@P1 and baz=@P2 and status&lt;&gt;@P3 and status&lt;&gt;@P4                                           </inputbuf>
    </process>
</process-list>
<resource-list>
    <pagelock fileid="1" pageid="463762" dbid="7" subresource="FULL" objectname="mydb.dbo.foo" id="lock2011f3b5b80" mode="SIX" associatedObjectId="72057594055753728">
        <owner-list>
            <owner id="process2013de9b848" mode="SIX"/>
        </owner-list>
        <waiter-list>
            <waiter id="process200e2c2cca8" mode="IX" requestType="wait"/>
        </waiter-list>
    </pagelock>
    <pagelock fileid="1" pageid="481017" dbid="7" subresource="FULL" objectname="mydb.dbo.bar" id="lock200bdfb6980" mode="IX" associatedObjectId="72057594042974208">
        <owner-list>
            <owner id="process200e2c2cca8" mode="IX"/>
        </owner-list>
        <waiter-list>
            <waiter id="process2013de9b848" mode="S" requestType="convert"/>
        </waiter-list>
    </pagelock>
</resource-list>
</deadlock>
Run Code Online (Sandbox Code Playgroud)

具有不同页面的两个不同表如何创建页面锁死锁?

Jos*_*ell 10

这是一个典型的死锁,但很难在 SSMS 中或仅通过查看 XML 来判断。这两个表都在两个会话中被访问。

SentryOne 计划资源管理器中查看它更容易一些,您还可以点击“播放”按钮来观看死锁的时间安排。这是一个屏幕截图:

Plan Explorer 中死锁图的屏幕截图

这是发生的事情:

  1. 会话 93 运行一条语句,该语句在 dbo.foo
    • 此语句显示在<inputbuf>元素中
  2. 会话 77 运行一条语句,该语句在 dbo.bar
    • 此语句显示在<inputbuf>元素中
  3. 会话 77 运行一条语句,尝试dbo.foo在第 1 步中锁定 的页面上获取 IX 锁
    • 这是update foo...where id=@P25 and version=@P26该语句包含在<inputbuf>元素
  4. 会话 93 运行一条语句,*尝试dbo.bar在第 2 步中锁定 的页面上获取 S 锁
    • 这是select count(*) as ct from bar where...包括在所述<inputbuf>元件

因此陷入僵局。

您需要跟踪运行这些查询的代码,并查看每个批处理/事务中正在运行的其他语句。<inputbuf>元素中显示的查询文本可能会产生误导,因为它可以被截断,并且它不一定显示在事务期间执行的每个语句(您可以在 Erik Darling 的博客上看到一个很好的例子)。

我注意到连接使用了“implicit_transaction”,这是 SQL Server JDBC 驱动程序的一个不幸的“特性”。setAutoCommit(true)如果您不打算在事务中执行此连接的所有语句,您可能希望调用。这可能会解决您的死锁问题。

如果您确实需要在事务中完成所有这些工作,避免这种死锁的经典建议是确保以相同的顺序获取和释放锁。如果没有看到您的代码,很难比这更具体,但是如果您有任何问题,请告诉我。