【问题标题】:MS SQL Server - Frequent Deadlock IssuesMS SQL Server - 常见的死锁问题
【发布时间】:2017-11-15 05:31:07
【问题描述】:

目标数据库: MS SQL Server 2012

要求:在表格中维护用户工作量的摘要。以下是用于维护此摘要的表结构。表正在更新分配给用户的工作项状态的修改。

  • PARTICIPANT_ID varchar NO PK,唯一非聚集索引
  • OFFERED_COUNT 个数字 YES
  • ALLOCATED_COUNT 个数字是
  • STARTED_COUNT 个数字 YES
  • SUSPENDED_COUNT 个数字是

问题:我们在这张桌子上经常遇到死锁。特此附上死锁图供参考。

目前已完成调查:

  • 死锁中的目标行(更新)是相互独立的,因此没有一个事务等待另一个事务放弃锁定的问题。即使插入到表中也会成为死锁的一部分。
  • 同一事务在 Oracle 中处理类似的并发负载。
  • 怀疑该表被锁定而不是特定行,我们甚至尝试使用以下命令禁用表的锁升级,但死锁仍然存在。

ALTER TABLE Table_name SET(LOCK_ESCALATION DISABLE)

死锁图

<deadlock>
 <victim-list>
  <victimProcess id="process133f03d498" />
 </victim-list>
 <process-list>
  <process id="process133f03d498" taskpriority="0" logused="3308" waitresource="RID: 11:1:819250:71" waittime="2377" ownerId="29578701" transactionname="implicit_transaction" lasttranstarted="2017-11-09T09:29:43.397" XDES="0x10b0b716a8" lockMode="U" schedulerid="6" kpid="7332" status="suspended" spid="210" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-11-09T09:29:45.977" lastbatchcompleted="2017-11-09T09:29:45.397" lastattention="1900-01-01T00:00:00.397" clientapp="jTDS" hostname="PAWSPPAS02" hostpid="123" loginname="STG_SUITE_SSO" isolationlevel="read committed (2)" xactid="29578701" currentdb="11" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
   <executionStack>
    <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x02000000ee409c1fd278abb7f476707399656cbedec9d1960000000000000000000000000000000000000000">
update [RS_WI_PARTICIPANT_SUMM] set [ALLOCATED_COUNT]=[ALLOCATED_COUNT]-1 where [PARTICIPANT_ID]= @P0    </frame>
   </executionStack>
   <inputbuf>
(@P0 nvarchar(4000))update [RS_WI_PARTICIPANT_SUMM] set [ALLOCATED_COUNT]=[ALLOCATED_COUNT]-1 where [PARTICIPANT_ID]= @P0    </inputbuf>
  </process>
  <process id="processc70140188" taskpriority="0" logused="85392" waitresource="RID: 11:1:819250:30" waittime="2196" ownerId="29574187" transactionname="implicit_transaction" lasttranstarted="2017-11-09T09:29:16.427" XDES="0x1150f5d6a8" lockMode="U" schedulerid="4" kpid="384" status="suspended" spid="141" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-11-09T09:29:46.157" lastbatchcompleted="2017-11-09T09:29:45.470" lastattention="1900-01-01T00:00:00.470" clientapp="jTDS" hostname="PAWSPPAS02" hostpid="123" loginname="STG_SUITE_SSO" isolationlevel="read committed (2)" xactid="29574187" currentdb="11" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128058">
   <executionStack>
    <frame procname="adhoc" line="1" stmtstart="40" sqlhandle="0x020000001c3c261d27cfa98e13a9cb06f6c30e40cfcaa4a50000000000000000000000000000000000000000">
update [RS_WI_PARTICIPANT_SUMM] set [ALLOCATED_COUNT]=[ALLOCATED_COUNT]+1 where [PARTICIPANT_ID]= @P0    </frame>
   </executionStack>
   <inputbuf>
(@P0 nvarchar(4000))update [RS_WI_PARTICIPANT_SUMM] set [ALLOCATED_COUNT]=[ALLOCATED_COUNT]+1 where [PARTICIPANT_ID]= @P0    </inputbuf>
  </process>
 </process-list>
 <resource-list>
  <ridlock fileid="1" pageid="819250" dbid="11" objectname="STG_SUITE_SSO.dbo.RS_WI_PARTICIPANT_SUMM" id="lock1236714900" mode="X" associatedObjectId="72057599198953472">
   <owner-list>
    <owner id="processc70140188" mode="X" />
   </owner-list>
   <waiter-list>
    <waiter id="process133f03d498" mode="U" requestType="wait" />
   </waiter-list>
  </ridlock>
  <ridlock fileid="1" pageid="819250" dbid="11" objectname="STG_SUITE_SSO.dbo.RS_WI_PARTICIPANT_SUMM" id="lockdacb14f00" mode="X" associatedObjectId="72057599198953472">
   <owner-list>
    <owner id="process133f03d498" mode="X" />
   </owner-list>
   <waiter-list>
    <waiter id="processc70140188" mode="U" requestType="wait" />
   </waiter-list>
  </ridlock>
 </resource-list>
</deadlock>

【问题讨论】:

  • 这张表有索引吗?有多少索引包含[PARTICIPANT_ID] 作为第一列?我的理由是:在您的情况下,死锁是由于不同进程使用不同的索引而发生的。如果您添加索引提示以强制所有进程使用相同的索引,您的问题可能就会消失。 SQL Server对索引和表分别加锁,因此如果不同的进程使用不同的索引,可能会出现问题。
  • Alex,我们在 [PARTICIPANT_ID] 列上只有一个非聚集索引。感谢您的及时回复。
  • 桌子有多大?如果它足够小,那么简单的表扫描也是可能的。

标签: sql-server deadlock database-deadlocks


【解决方案1】:

一个线程更新了第 819250:71 行,现在想更新第 819250:30 行,另一个线程更新了第 819250:30 行,现在想更新第 819250:71 行。

我只能推测这里发生了什么(因为你省略了很多细节),但我的猜测是应用程序有一些逻辑在某种交换中更新两个“参与者”,并且在数据库内部交易,试图增加一个人的金额+1,然后减少另一个人的金额-1。但是,应用程序逻辑始终以相反的顺序执行此操作(线程在事务中执行 [A+=1, B-=1] 而另一个正在执行 [B+=1, A-=1])。没有模式设计可以防止这种死锁,只有应用程序逻辑发生变化。看看为什么并发反向更新如此频繁。

这似乎不是在 [PARTICIPANT_ID] 上使用非唯一非聚集索引的问题,因为在这种情况下,引擎会以相同的顺序为两个语句定位行 819250:71 和 819250:30并且不会陷入僵局。因此我的结论是,我们在一个事务中(至少)有两个更新语句,更新两个不同的行(两个 @P0 值),并且应用程序逻辑正在以相反的顺序从 2 个事务更新同一组参与者(可能是 2 个请求)。

此外:

<process id="498" "RID: 11:1:819250:71" lasttranstarted="2017-11-09T09:29:43.397" lastbatchstarted="2017-11-09T09:29:45.977" lastbatchcompleted="2017-11-09T09:29:45.397"... >
<process id="188" "RID: 11:1:819250:30" lasttranstarted="2017-11-09T09:29:16.427" lastbatchstarted="2017-11-09T09:29:46.157" lastbatchcompleted="2017-11-09T09:29:45.470" ...>

如果你看一下这两笔交易:

  • 进程 1 在 9:29:43.397 开始事务,在 9:29:45.397 完成一个批次,在 9:29:45.977 开始当前批次
  • 进程 2 在 9:29:16.427 开始事务,在 9:29:45.470 完成一个批次,在 9:29:46.157 开始当前批次

这个时间序列证实了我的假设。事务包含一系列批次,指示单独的更新(例如实体框架应用更改或类似),这些更新适用于不同的行(不同的 ID)。 ID 重叠,更新的应用顺序不一致(同样,像 EF 这样的 ORM 应用更改的典型行为)。

如果我的猜测是正确的,那么您应该先查看锁定更高级别的逻辑实体。如果这些是参与者,那么他们一定会参与一​​些事情,例如游戏。在更新参与者之前,锁定游戏:例如。在事务开始时在[GAME_ID] 上使用sp_getapplock。这样更新参与者的顺序就不再有死锁的危险,因为更新是为每个游戏序列化的。

其他解决方案是在应用更新之前对参与者进行排序,以便每个事务以相同的顺序更新行。

我们在 [PARTICIPANT_ID] 列上只有一个非聚集索引

几乎可以肯定这应该是一个聚集索引。很少有非聚集索引和堆有意义的情况。

【讨论】:

  • 感谢您的详细分析。我们确实详述了这种情况,但从业务角度来看,这是一种非常罕见的情况,即两个参与者的记录同时在不同的事务中更新,而死锁非常频繁。其次,如果表中不存在参与者记录,则事务将数据插入表中。有趣的是,我们甚至看到插入也参与了死锁。
  • 此外,我们尝试使用 Oracle 模拟场景,但我们无法重现任何死锁,因此我们怀疑它更多地与数据库有关而不是代码。
  • @Sandeep 在死锁之前了解所涉及的事务中运行的语句到底是什么会很有用。死锁信息清楚地表明在两个事务中都执行了先前的语句。
  • 我是w.r.t的初学者。 SQL 服务器。我相信 SQL Server 通过杀死一个低成本的参与进程来避免死锁。无论如何,我们之后是否可以找到有关参与事务和实际目标行的任何详细信息。如果您可以为此共享 SQL 语句,那将是很大的帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多