【问题标题】:Spring Batch - org.springframework.dao.DeadlockLoserDataAccessException in Azure SQL Server?Spring Batch - Azure SQL Server 中的 org.springframework.dao.DeadlockLoserDataAccessException?
【发布时间】:2022-01-20 19:58:17
【问题描述】:

我有一个使用 Azure SQL Server 作为数据库的 Spring Batch 应用程序。

Source 表有两个条目,用于每个 Store [0001] Eff_Date [2021-10-29] 和 ItemID [0000000000000] 的组合

类似

Store [0001] Eff_Date [2021-10-29] and ItemID [0000000000000]
Store [0001] Eff_Date [2021-10-29] and ItemID [0000000000000]

Store [0002] Eff_Date [2021-10-29] and ItemID [0000000000000]
Store [0002] Eff_Date [2021-10-29] and ItemID [0000000000000]

目标表有一个集群主键约束为Store + Eff_Date + ItemID

应用程序是这样设计的

  1. 插入记录
  2. 如果插入失败,更新记录

我在尝试处理上述记录时遇到以下错误

2021-12-15 03:42:51,134 DEBUG [SimpleAsyncTaskExecutor-1] ItemItemWriter - Writing record for [0001] Eff_Date [2021-10-29] Host Batch [0] UPC [0000000000000]
2021-12-15 03:42:51,134 DEBUG [SimpleAsyncTaskExecutor-1] ItemDaoImpl - Inserting New Item Data: ItemId [0000000000000] StoreNbr [0001] EffectiveDt [2021-10-29]

2021-12-15 03:42:51,136 DEBUG [SimpleAsyncTaskExecutor-1] ItemItemWriter - Writing record for [0001] Eff_Date [2021-10-29] Host Batch [0] UPC [0000000000000]
2021-12-15 03:42:51,136 DEBUG [SimpleAsyncTaskExecutor-1] ItemDaoImpl - Inserting New Item Data: ItemId [0000000000000] StoreNbr [0001] EffectiveDt [2021-10-29]
2021-12-15 03:42:51,139 DEBUG [SimpleAsyncTaskExecutor-1] ItemDaoImpl - Updating Existing Item Data: ItemId [0000000000000] StoreNbr [0001] EffectiveDt [2021-10-29]
2021-12-15 03:42:52,546 ERROR [SimpleAsyncTaskExecutor-1] ItemDaoImpl - An error occurred during item update for Item [0000000000000] ] Store [0001] ] Batch [0] ] Date [2021-10-29] :org.springframework.dao.DeadlockLoserDataAccessException: 

大部分更新都因 DeadlockLoserDataAccessException 而失败。

所以,我更新了 Insert & Update 语句,例如(有和没有 Begin Tran 和 commit Tran,结果是一样的)

  1. 开始将 Tran 插入到表中with (Tablock)... Commit tran
  2. 开始 Tran 更新表 with (Tablock) set commit Tran

也试过(有和没有 Begin Tran & commit Tran,结果是一样的):

  1. 开始将 Tran 插入到表中使用 (SERIALIZABLE)...提交 tran
  2. 开始 Tran 更新表 with (SERIALIZABLE) set commit Tran

现在更新语句不再因 DeadlockLoserDataAccessException 而失败,但是很少有插入语句抛出 DeadlockLoserDataAccessException 异常

 An error occurred during new item insert for Item [0000060923410] ] Store [0056]  
 An error occurred during new item insert for Item [0000060923410] ] Store [0052]  
 An error occurred during new item insert for Item [0000060923410] ] Store [3278]  
 An error occurred during new item insert for Item [0000060923410] ] Store [0052]  
 An error occurred during new item insert for Item [0000060923410] ] Store [3284]  
 An error occurred during new item insert for Item [0000060923410] ] Store [3278]  
 An error occurred during new item insert for Item [0000060923410] ] Store [3290]  
 An error occurred during new item insert for Item [0000060923410] ] Store [3284]  
 An error occurred during new item insert for Item [0001030010279] ] Store [3278]  
 An error occurred during new item insert for Item [0001030010279] ] Store [3284]  
 An error occurred during new item insert for Item [0000060923410] ] Store [3290]  
 An error occurred during new item insert for Item [0000954242689] ] Store [0052]  
 An error occurred during new item insert for Item [0001030010279] ] Store [3290]  
 An error occurred during tag request insert for Item [0001030010279] ] Store [3290]  
 An error occurred during new item insert for Item [0001030053664] ] Store [3284]  
 An error occurred during new item insert for Item [0001030080895] ] Store [3284]  
 An error occurred during tag request insert for Item [0001030080895] ] Store [3284]  

可能是什么原因和解决方法?

注意:我尝试从插入语句中删除 with (tablock) 但更新语句开始抛出死锁错误。

来自 SQL Server 的死锁详细信息

<deadlock>
  <victim-list>
    <victimProcess id="process1d67b529c28" />
  </victim-list>
  <process-list>
    <process id="process1d67b529c28" taskpriority="0" logused="0" waitresource="OBJECT: 6:1442156233:0 " waittime="7673" ownerId="101271231" transactionname="implicit_transaction" lasttranstarted="2021-12-20T13:03:47.363" XDES="0x1d68c330428" lockMode="X" schedulerid="8" kpid="23916" status="suspended" spid="172" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-20T13:03:47.363" lastbatchcompleted="2021-12-20T13:03:47.273" lastattention="1900-01-01T00:00:00.273" clientapp="Microsoft JDBC Driver for SQL Server" hostname="myLaptop" hostpid="0" loginname="myDBUser" isolationlevel="read committed (2)" xactid="101271231" currentdb="6" currentdbname="myDBUser" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
      <executionStack>
        <frame procname="unknown" queryhash="0x7c82d557079e2a63" queryplanhash="0xe907b104918dcca3" line="1" stmtstart="6176" stmtend="12328" sqlhandle="0x020000003a328135b9a6da13379eb4d245948c00142904ee0000000000000000000000000000000000000000">
unknown    </frame>
        <frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown    </frame>
      </executionStack>
      <inputbuf>
(@P0 nvarchar(4000),@P1 nvarchar(4000),@P2 date,@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 nvarchar(4000),@P7 int,@P8 nvarchar(4000),@P9 nvarchar(4000),@P10 nvarchar(4000),@P11 nvarchar(4000),@P12 decimal(38,4),@P13 nvarchar(4000),@P14 smallint,@P15 decimal(38,4),@P16 decimal(38,2),@P17 smallint,@P18 decimal(38,0),@P19 decimal(38,0),@P20 smallint,@P21 nvarchar(4000),@P22 nvarchar(4000),@P23 nvarchar(4000),@P24 smallint,@P25 decimal(38,2),@P26 smallint,@P27 decimal(38,0),@P28 decimal(38,0),@P29 decimal(38,2),@P30 smallint,@P31 smallint,@P32 nvarchar(4000),@P33 nvarchar(4000),@P34 nvarchar(4000),@P35 decimal(38,2),@P36 decimal(38,4),@P37 nvarchar(4000),@P38 nvarchar(4000),@P39 nvarchar(4000),@P40 nvarchar(4000),@P41 nvarchar(4000),@P42 nvarchar(4000),@P43 nvarchar(4000),@P44 nvarchar(4000),@P45 nvarchar(4000),@P46 nvarchar(4000),@P47 nvarchar(4000),@P48 nvarchar(4000),@P49 nvarchar(4000),@P50 nvarchar(4000),@P51 nvarchar(4000),@P52 nvarchar(4000),@P53 nvarchar(4000),@P54 nvarchar(4000),@P55 n   </inputbuf>
    </process>
    <process id="process1d67b575468" taskpriority="0" logused="0" waitresource="OBJECT: 6:1442156233:7 " waittime="2498" ownerId="101271602" transactionname="implicit_transaction" lasttranstarted="2021-12-20T13:04:11.150" XDES="0x1d55a758428" lockMode="X" schedulerid="1" kpid="62644" status="suspended" spid="171" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-20T13:04:11.150" lastbatchcompleted="2021-12-20T13:03:46.487" lastattention="1900-01-01T00:00:00.487" clientapp="Microsoft JDBC Driver for SQL Server" hostname="myLaptop" hostpid="0" loginname="myDBUser" isolationlevel="read committed (2)" xactid="101271602" currentdb="6" currentdbname="myDBUser" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
      <executionStack>
        <frame procname="unknown" queryhash="0x7c82d557079e2a63" queryplanhash="0xe907b104918dcca3" line="1" stmtstart="6176" stmtend="12328" sqlhandle="0x02000000314353379a6b206b3eb6880ab89e1e4d79d124220000000000000000000000000000000000000000">
unknown    </frame>
      </executionStack>
      <inputbuf>
(@P0 nvarchar(4000),@P1 nvarchar(4000),@P2 date,@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 nvarchar(4000),@P7 int,@P8 nvarchar(4000),@P9 nvarchar(4000),@P10 nvarchar(4000),@P11 nvarchar(4000),@P12 decimal(38,4),@P13 nvarchar(4000),@P14 smallint,@P15 decimal(38,4),@P16 decimal(38,0),@P17 smallint,@P18 decimal(38,0),@P19 decimal(38,0),@P20 smallint,@P21 nvarchar(4000),@P22 nvarchar(4000),@P23 nvarchar(4000),@P24 smallint,@P25 decimal(38,0),@P26 smallint,@P27 decimal(38,0),@P28 decimal(38,0),@P29 decimal(38,0),@P30 smallint,@P31 smallint,@P32 nvarchar(4000),@P33 nvarchar(4000),@P34 nvarchar(4000),@P35 decimal(38,0),@P36 decimal(38,4),@P37 nvarchar(4000),@P38 nvarchar(4000),@P39 nvarchar(4000),@P40 nvarchar(4000),@P41 nvarchar(4000),@P42 nvarchar(4000),@P43 nvarchar(4000),@P44 nvarchar(4000),@P45 nvarchar(4000),@P46 nvarchar(4000),@P47 nvarchar(4000),@P48 nvarchar(4000),@P49 nvarchar(4000),@P50 nvarchar(4000),@P51 nvarchar(4000),@P52 nvarchar(4000),@P53 nvarchar(4000),@P54 nvarchar(4000),@P55 n   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <objectlock lockPartition="0" objid="1442156233" subresource="FULL" dbid="6" objectname="3d6766e5-31cc-4898-8415-e27d1c16d503.myDBUser.STORE_ITEM" id="lock1d655388f80" mode="X" associatedObjectId="1442156233">
      <owner-list>
        <owner id="process1d67b575468" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process1d67b529c28" mode="X" requestType="wait" />
      </waiter-list>
    </objectlock>
    <objectlock lockPartition="7" objid="1442156233" subresource="FULL" dbid="6" objectname="3d6766e5-31cc-4898-8415-e27d1c16d503.myDBUser.STORE_ITEM" id="lock1d6350c9b80" mode="IX" associatedObjectId="1442156233">
      <owner-list>
        <owner id="process1d67b529c28" mode="IX" />
      </owner-list>
      <waiter-list>
        <waiter id="process1d67b575468" mode="X" requestType="wait" />
      </waiter-list>
    </objectlock>
  </resource-list>
</deadlock>

另一个

<deadlock>
  <victim-list>
    <victimProcess id="process1d67b573088" />
  </victim-list>
  <process-list>
    <process id="process1d67b573088" taskpriority="0" logused="0" waitresource="OBJECT: 6:1442156233:4 " waittime="2510" ownerId="101411096" transactionname="implicit_transaction" lasttranstarted="2021-12-20T13:40:14.337" XDES="0x1d68c984428" lockMode="X" schedulerid="6" kpid="15084" status="suspended" spid="145" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-20T13:40:14.337" lastbatchcompleted="2021-12-20T13:40:14.260" lastattention="1900-01-01T00:00:00.260" clientapp="Microsoft JDBC Driver for SQL Server" hostname="myLaptop" hostpid="0" loginname="myDBUser" isolationlevel="read committed (2)" xactid="101411096" currentdb="6" currentdbname="myDBUser" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
      <executionStack>
        <frame procname="unknown" queryhash="0x7c82d557079e2a63" queryplanhash="0xe907b104918dcca3" line="1" stmtstart="6176" stmtend="12328" sqlhandle="0x020000003a328135b9a6da13379eb4d245948c00142904ee0000000000000000000000000000000000000000">
unknown    </frame>
        <frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown    </frame>
      </executionStack>
      <inputbuf>
(@P0 nvarchar(4000),@P1 nvarchar(4000),@P2 date,@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 nvarchar(4000),@P7 int,@P8 nvarchar(4000),@P9 nvarchar(4000),@P10 nvarchar(4000),@P11 nvarchar(4000),@P12 decimal(38,4),@P13 nvarchar(4000),@P14 smallint,@P15 decimal(38,4),@P16 decimal(38,2),@P17 smallint,@P18 decimal(38,0),@P19 decimal(38,0),@P20 smallint,@P21 nvarchar(4000),@P22 nvarchar(4000),@P23 nvarchar(4000),@P24 smallint,@P25 decimal(38,2),@P26 smallint,@P27 decimal(38,0),@P28 decimal(38,0),@P29 decimal(38,2),@P30 smallint,@P31 smallint,@P32 nvarchar(4000),@P33 nvarchar(4000),@P34 nvarchar(4000),@P35 decimal(38,2),@P36 decimal(38,4),@P37 nvarchar(4000),@P38 nvarchar(4000),@P39 nvarchar(4000),@P40 nvarchar(4000),@P41 nvarchar(4000),@P42 nvarchar(4000),@P43 nvarchar(4000),@P44 nvarchar(4000),@P45 nvarchar(4000),@P46 nvarchar(4000),@P47 nvarchar(4000),@P48 nvarchar(4000),@P49 nvarchar(4000),@P50 nvarchar(4000),@P51 nvarchar(4000),@P52 nvarchar(4000),@P53 nvarchar(4000),@P54 nvarchar(4000),@P55 n   </inputbuf>
    </process>
    <process id="process1d67ab88108" taskpriority="0" logused="0" waitresource="OBJECT: 6:1442156233:0 " waittime="2510" ownerId="101410953" transactionname="implicit_transaction" lasttranstarted="2021-12-20T13:40:12.427" XDES="0x1d595d04428" lockMode="X" schedulerid="5" kpid="13796" status="suspended" spid="146" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2021-12-20T13:40:12.427" lastbatchcompleted="2021-12-20T13:40:12.370" lastattention="1900-01-01T00:00:00.370" clientapp="Microsoft JDBC Driver for SQL Server" hostname="myLaptop" hostpid="0" loginname="myDBUser" isolationlevel="read committed (2)" xactid="101410953" currentdb="6" currentdbname="myDBUser" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
      <executionStack>
        <frame procname="unknown" queryhash="0x7c82d557079e2a63" queryplanhash="0xe907b104918dcca3" line="1" stmtstart="6176" stmtend="12328" sqlhandle="0x020000003a328135b9a6da13379eb4d245948c00142904ee0000000000000000000000000000000000000000">
unknown    </frame>
        <frame procname="unknown" queryhash="0x0000000000000000" queryplanhash="0x0000000000000000" line="1" sqlhandle="0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000">
unknown    </frame>
      </executionStack>
      <inputbuf>
(@P0 nvarchar(4000),@P1 nvarchar(4000),@P2 date,@P3 nvarchar(4000),@P4 nvarchar(4000),@P5 nvarchar(4000),@P6 nvarchar(4000),@P7 int,@P8 nvarchar(4000),@P9 nvarchar(4000),@P10 nvarchar(4000),@P11 nvarchar(4000),@P12 decimal(38,4),@P13 nvarchar(4000),@P14 smallint,@P15 decimal(38,4),@P16 decimal(38,2),@P17 smallint,@P18 decimal(38,0),@P19 decimal(38,0),@P20 smallint,@P21 nvarchar(4000),@P22 nvarchar(4000),@P23 nvarchar(4000),@P24 smallint,@P25 decimal(38,2),@P26 smallint,@P27 decimal(38,0),@P28 decimal(38,0),@P29 decimal(38,2),@P30 smallint,@P31 smallint,@P32 nvarchar(4000),@P33 nvarchar(4000),@P34 nvarchar(4000),@P35 decimal(38,2),@P36 decimal(38,4),@P37 nvarchar(4000),@P38 nvarchar(4000),@P39 nvarchar(4000),@P40 nvarchar(4000),@P41 nvarchar(4000),@P42 nvarchar(4000),@P43 nvarchar(4000),@P44 nvarchar(4000),@P45 nvarchar(4000),@P46 nvarchar(4000),@P47 nvarchar(4000),@P48 nvarchar(4000),@P49 nvarchar(4000),@P50 nvarchar(4000),@P51 nvarchar(4000),@P52 nvarchar(4000),@P53 nvarchar(4000),@P54 nvarchar(4000),@P55 n   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <objectlock lockPartition="4" objid="1442156233" subresource="FULL" dbid="6" objectname="3d6766e5-31cc-4898-8415-e27d1c16d503.myDBUser.STORE_ITEM" id="lock1d6598a7280" mode="IX" associatedObjectId="1442156233">
      <owner-list>
        <owner id="process1d67ab88108" mode="IX" />
      </owner-list>
      <waiter-list>
        <waiter id="process1d67b573088" mode="X" requestType="wait" />
      </waiter-list>
    </objectlock>
    <objectlock lockPartition="0" objid="1442156233" subresource="FULL" dbid="6" objectname="3d6766e5-31cc-4898-8415-e27d1c16d503.myDBUser.STORE_ITEM" id="lock1d5b1706a00" mode="X" associatedObjectId="1442156233">
      <owner-list>
        <owner id="process1d67b573088" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process1d67ab88108" mode="X" requestType="wait" />
      </waiter-list>
    </objectlock>
  </resource-list>
</deadlock>

注意: 我已将我的 JDBC URL 附加到 sendStringParametersAsUnicode=false

我验证了表格,发现只有 char、varchar、int 和 Datetime2 列。

jdbctemplate.update 也接受查询、值和数据类型。数据类型在应用程序中正确定义。在应用程序的任何地方都找不到 nvarchar 的引用。

【问题讨论】:

  • 尝试显式可序列化事务。 T-SQL 示例:SET XACT_ABORT ON;BEGIN TRAN;UPDATE target WITH(SERIALZABLE)...;IF @@ROWCOUNT = 0 INSERT target...; COMMIT;.
  • SERIALIZABLE 提示(或会话级别 SERIALIZABLE 事务隔离级别)将阻止其他会话插入或更新具有相同键的行(阻止它们),直到事务提交。见SET TRANSACTION ISOLATION LEVEL documentattion
  • 通过这个简短的小交易,我预计不会出现性能问题。事实上,我希望它比应用程序中单独的插入/更新命令更快。
  • 确保参数和列数据类型匹配。字符串参数数据类型显示nvarchar,如果引用的键列是varchar,则可能会阻止有效的索引使用,从而导致容易出现死锁的扫描。查看 UPDATE 执行计划,看看它是否正在扫描。
  • 如果列数据类型是varchar,那么是的,您应该将参数数据类型更改为varchar。

标签: java sql-server spring azure-sql-database database-deadlocks


【解决方案1】:

错误:在项目 [0000000000000] ] 商店 [0001] ] 批次 [0] ] 日期 [2021-10-29] 的项目更新期间发生错误:org.springframework.dao.DeadlockLoserDataAccessException:

默认 Transact-SQL 语句在完成时提交或回滚(自动提交事务)。

解决与其他进程修改相关的问题的最常见解决方案是将易受攻击的语句包装在事务中(作为一个执行全部或全部执行)。

理论上,使用事务可以隔离其他并发活动的影响。

实际上,我们只有一定程度的隔离,具体取决于事务隔离级别。

Serializable 是标准事务隔离级别中最孤立的。它的行为就像每个 SQL 事务在下一个 SQL 事务开始之前执行到完成。

请注意,它与序列化执行不同,其中每个事务实际上在下一个事务开始之前专门运行到完成!可串行化隔离只需要具有与串行执行相同的效果(以某种未指定的顺序)。

例如,如果我们使用两个事务对两个不相关的行执行 UPDATE,则可序列化隔离允许并行执行这些事务,因为结果与一个接一个地执行事务相同。

但是如果我们有一个约束(如主键),它强制行之间的唯一性关系 - 你不能设置(更新/插入)一个存在的值。这意味着服务器必须首先确认该值没有被使用(SQL Server 是“智能”的,不一定要扫描整个表,但它必须强制所有值的唯一性)。

集群主键约束为 Store + Eff_Date + ItemID

这会强制所有行之间以及在您的情况下所有列 [Store]、[Eff_Date]、[ItemID] 之间的关系

大部分更新都因 DeadlockLoserDataAccessException 而失败。

这不是直接来自 SQL Server,而是来自 Spring 框架

要获得有关 SQL Side 的更多信息,您应该监控 SQL Server 错误。当您没有错误来源而只有“二手货”时,很难监控问题来源"信息。

一般而言,从 SQL Server 端来看,等待/锁定是有意义的,正如我在上面解释的那样,您使用此 Promary 键所实施的关系。这些等待/锁定也可能导致死锁

使用 (Tablock) 插入表格

这意味着您从 INSERT 开始到结束锁定整个表!在大多数情况下,这听起来是个非常糟糕的主意。

使用 (SERIALIZABLE) 插入表...现在更新语句不再因 DeadlockLoserDataAccessException 而失败,但很少有插入语句引发 DeadlockLoserDataAccessException 异常

再次,我不知道什么是 DeadlockLoserDataAccessException 因为它不是源问题,而是客户端的解释(连接 SQL Server 的 spring 框架),但是在插入语句中会出现死锁是有道理的因为你有更新需要 X 锁(独占锁)

检查我上面的解释并记住可序列化隔离与序列化执行不同!

可能是什么原因和解决方法?

(1)首先,从监控问题的根源开始,而不是spring框架对问题的解释。

直接监控SQL Server端的等待、锁、死锁。有多种工具可供 Google 为您找到完成这项常见任务的最佳教程。

如果插入失败,更新记录

(2) 不要使用 INSERT,如果失败,那么尝试使用 UPDATE 只需使用 MERGE 查询。这个单一查询不需要与另一个锁定相同资源的查询相匹配,并且可能会在途中提供更好的性能

(3) 尝试删除您配置的所有查询提示并让服务器管理查询,一旦您开始使用 MERGE。

【讨论】:

  • 请注意,我关注的是SQL Sever端而不是spring框架,原因有两个:(1)我对spring框架完全不熟悉,(2)问题来自客户端请求但问题的根源在于 SQL Server 端。