【问题标题】:Hibernate ORM T-SQL Queries causing Deadlocks in SQL Server 2016在 SQL Server 2016 中导致死锁的休眠 ORM T-SQL 查询
【发布时间】:2019-12-12 06:59:00
【问题描述】:

我有一个 SQL Server 2016 数据库,我们一直使用 Hibernate Java (ORM) 作为前端来访问数据库。

以下情况导致死锁:其中一个事务正在执行涉及连接中的许多表的选择操作,如下所示:

select * 
from table 1 
inner join table2_ on table1PK = table2FK 
left outer join table3 on table1PK = table3FK 
left outer join table4_ on table4FK = table3PK 
... 
where Table1PK = '1234' and table3.Status <> 'NA' 

另一个事务正在上述连接中涉及的任何表上执行UPDATE

Update Table4 
set columnA = 'XYZ' 
where Table4PK = 'ABC'

这两个事务之间经常出现死锁。

以下是收集到的分析:

  1. Select 语句连接中涉及的所有表都在 PK FK 关系上
  2. 所有 PK 和 FK 列都有索引(PK 集群和 FK 非集群)
  3. 由于查询是从 ORM Hibernate 生成的,因此它会检索 Select 语句中的所有列
  4. 执行计划执行非聚集索引查找,然后执行所有表的键查找
  5. Select 语句使用 Shared S 锁,Update 语句使用 Exclusive X 锁
  6. 以上表格禁用页级锁
  7. 统计数据和索引是最新的/经常更新

由于应用程序逻辑,无法使用 READ Committed Snapshot 和 Read Uncommitted 隔离级别。

任何避免上述场景死锁的建议。

死锁跟踪:

    <deadlock>
     <victim-list>
      <victimProcess id="process2ba81fa8c8" />
     </victim-list>
     <process-list>
      <process id="process2ba81fa8c8" taskpriority="0" logused="0"                     
    waitresource="KEY: 7:72057597045899264 (5a800aed3519)" waittime="2537" ownerId="1798795" transactionname="SELECT" lasttranstarted="2019-12-12T13:17:02.820" XDES="0x2841883a40" lockMode="S" schedulerid="6" kpid="10140" status="suspended" spid="429" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2019-12-12T13:17:02.823" lastbatchcompleted="2019-12-12T13:17:02.820" lastattention="1900-01-01T00:00:00.820" clientapp="Microsoft JDBC Driver for SQL Server" hostname="" hostpid="0" loginname="" isolationlevel="read committed (2)" xactid="1798795" currentdb="" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
         <executionStack>
          <frame procname="adhoc" line="1" stmtstart="24" stmtend="33928" sqlhandle="0x02000000a700b227b6df842918a30ca7f4b4cd5ff967f9810000000000000000000000000000000000000000">
      unknown    </frame>
          <frame procname="unknown" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
      unknown    </frame>
         </executionStack>
         <inputbuf>
(@P0 bigint)select ... from ABC inner join XYZ  </inputbuf>
        </process>
        <process id="process2ba81f8108" taskpriority="0" logused="5812788" waitresource="KEY: 7:72057596983508992 (c750150781e8)" waittime="2306" ownerId="1795488" transactionname="implicit_transaction" lasttranstarted="2019-12-12T13:17:02.180" XDES="0x2bb290d478" lockMode="X" schedulerid="5" kpid="9876" status="suspended" spid="382" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2019-12-12T13:17:03.053" lastbatchcompleted="2019-12-12T13:17:03.053" lastattention="1900-01-01T00:00:00.053" clientapp="Microsoft JDBC Driver for SQL Server" hostname="" hostpid="0" loginname="" isolationlevel="read committed (2)" xactid="1795488" currentdb="" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
         <executionStack>
          <frame procname="adhoc" line="1" stmtstart="1462" stmtend="3664" sqlhandle="0x020000007bd3bd2f098d8e394643ad11df2b1027b5b67fde0000000000000000000000000000000000000000">
unknown    </frame>
         </executionStack>
               <inputbuf>(@P0 bigint,@P1 datetime2,@P2 datetime2,@P3 varchar(8000),@P4 varchar(8000),@P5 datetime2,)update [dbo].[XYZ] set []=@P0, []=@P1, []=@P2, []=@P3, []=@P4,</inputbuf>
              </process>
             </process-list>
             <resource-list>
        <keylock hobtid="72057597045899264" dbid="7" objectname="DB.dbo.ABC" indexname="IX_ABC_XYZFK" id="lock2afc8ca500" mode="X" associatedObjectId="72057597045899264">
         <owner-list>
          <owner id="process2ba81f8108" mode="X" />
         </owner-list>
         <waiter-list>
          <waiter id="process2ba81fa8c8" mode="S" requestType="wait" />
         </waiter-list>
        </keylock>
        <keylock hobtid="72057596983508992" dbid="7" objectname="DB.dbo.XYZ" indexname="PK_XYZ" id="lock2afd023080" mode="U" associatedObjectId="72057596983508992">
         <owner-list>
          <owner id="process2ba81fa8c8" mode="S" />
         </owner-list>
         <waiter-list>
          <waiter id="process2ba81f8108" mode="X" requestType="convert" />
         </waiter-list>
        </keylock>
       </resource-list>
      </deadlock>

【问题讨论】:

  • 发布你的死锁图,因为它准确地解释了哪些资源正在死锁。
  • 您好 Dale,由于禁用了页面级锁定,因此在密钥级别发生了死锁。涉及的键是表中的 PK 和 FK
  • 发布你的死锁图 :)
  • 而且这方面的专业知识在dba.stackexchange.com上比较高
  • 明确 UPDATE 正在修改哪些列,以及 CASCADE UPDATE 是否有任何外键。以及 UPDATING 事务是否在同一事务中进行过任何先前的更新。不清楚为什么 UPDATEing 进程在表 ABC 的索引上拥有 X 锁。

标签: sql-server orm


【解决方案1】:

我假设您的表实际上并不称为 ABC 和 XYZ。抛开混淆,我会回顾你提到的地方:WHERE Table4PK = 'ABC'

仔细检查您的数据类型是否一致。例如,如果您的 PK 是数字并且您的 WHERE 子句传递一个字符串,则 SQL 有时可能会扫描超出其需要的数据。

死锁将发生在死锁情况下,即数据尝试读取而数据尝试写入同一位置。您提到您的应用程序逻辑不允许您阅读未提交的内容。您能否更新您的应用程序逻辑以在发生死锁错误时静默重试选择?

此外,您还需要查看磁盘性能是否存在 IO 瓶颈。如果数据可以更快地移动,那么它就不太可能竞争。例如,如果遇到写入瓶颈,可以考虑将索引移动到不同的文件组和磁盘。

【讨论】:

  • 嗨,肖恩,我已经仔细检查了 PK 和 FK 数据类型都是数字的,并且子句也传递了一个数值。我的大多数 NC 索引都在执行索引查找操作和键查找(由于休眠而从表中获取所有列)。
  • 请澄清“您能否更新您的应用程序逻辑以在发生死锁错误时静默重试选择?”是什么意思?
  • 所以您从数据管理的角度来看:如何摆脱死锁。但总体症状是某处应用程序代码尝试使用 ORM 代码读取数据并从数据库返回死锁错误?当该应用程序收到死锁错误时会发生什么,它会将其变成异常或错误吗?我想知道在收到数据库死锁错误后,是否可以更新代码以在第一次尝试被选为死锁牺牲品后重试选择/读取操作。这会让它看起来只是一个延迟。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-01
  • 1970-01-01
  • 2018-06-13
  • 2015-03-21
相关资源
最近更新 更多