【问题标题】:Deadlock from Range locks on primary key indexRange 死锁锁在主键索引上
【发布时间】:2018-03-16 16:38:07
【问题描述】:

我在一张桌子上遇到了奇怪的死锁(几张桌子,但它们都很相似),无论我多么努力地研究范围锁,我都无法理解它。一个进程始终只有SELECT 并且正在使用 RangeS-U,然后尝试将其转换为 RangeS-S。为什么 Select 会把 RangeS-U 放在首位?其他进程执行UPDATE 并采用 RangeX-X 并尝试转换为 X。有时第二个进程有 X 并尝试获取 X(或者我正在错误地读取图表)。主键索引字段永远不会更新,并且表上没有其他索引。业务流程的方式,我可以确定 select 和 update 都在处理有很大重叠的记录。如果需要,可以提供更多信息,表架构和死锁图如下。

表格 -

CREATE TABLE [dbo].[DeadlockTable] (
    [ID] [bigint] NOT NULL ,
    [Hour] [tinyint] NOT NULL ,
    [Status] [varchar] (32) COLLATE Latin1_General_BIN NOT NULL ,
    [MW] [decimal](19, 6) NULL ,
    [CreationUserID] [int] NULL ,
    [CreationTime] [datetime] NULL ,
    [ModificationUserID] [int] NULL ,
    [ModificationTime] [datetime] NULL ,
    [SubmissionUserID] [int] NULL ,
    [SubmissionTime] [datetime] NULL ,
    [ConfigurationID] [varchar] (64) COLLATE Latin1_General_BIN NULL ,
    CONSTRAINT [PK_DeadlockTable] PRIMARY KEY  CLUSTERED 
    (
        [ID],
        [Hour]
    )  ON [PRIMARY] 
) ON [PRIMARY]
GO

死锁 XML -

<deadlock-list>
    <deadlock victim="process5e13b88">
        <process-list>
            <process id="process5e13b88" taskpriority="0" logused="0" waitresource="KEY: 22:72057594344112128 (6123af010c07)" waittime="2978" ownerId="265837907" transactionname="INSERT" lasttranstarted="2017-09-01T10:04:10.767" XDES="0x16fb323b0" lockMode="RangeS-S" schedulerid="2" kpid="5316" status="suspended" spid="83" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-09-01T10:04:10.430" lastbatchcompleted="2017-09-01T10:04:09.907" clientapp="..." hostname="..." hostpid="7980" loginname="..." isolationlevel="serializable (4)" xactid="265837907" currentdb="22" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
                <executionStack>
                    <frame procname="adhoc" line="40" stmtstart="3776" stmtend="7404" sqlhandle="0x020000009555563118dcb9a28a078c9faa795d95341a9119">
                        INSERT INTO #TempTable (
                        ...
                        )
                        SELECT DISTINCT
                        ...
                        FROM Table1 B
                        LEFT JOIN DeadlockTable P ON B.[ID] = {more joins, got cropped}
                    </frame>
                    <frame procname="mssqlsystemresource.sys.sp_executesql" line="1" sqlhandle="0x0400ff7fbe80662601000000000000000000000000000000">
                        sp_executesql
                    </frame>
                    <frame procname="EXEC SPROC" line="386" stmtstart="28814" stmtend="29462" sqlhandle="0x03001600afff210aad8d3f01d0a700000100000000000000">
                        EXEC sp_executesql ...
                    </frame>
                    <frame procname="adhoc" line="1" sqlhandle="0x010016009a0b8926e0fb577e010000000000000000000000">
                        EXEC SPROC ...
                    </frame>
                </executionStack>
                <inputbuf>
                    EXEC SPROC ...
                </inputbuf>
            </process>
            <process id="process5e45b88" taskpriority="0" logused="236" waitresource="KEY: 22:72057594344112128 (55ed2fdeaf5a)" waittime="3130" ownerId="265839001" transactionname="UPDATE" lasttranstarted="2017-09-01T10:04:14.840" XDES="0x484c171b0" lockMode="X" schedulerid="7" kpid="4852" status="suspended" spid="90" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-09-01T10:04:14.817" lastbatchcompleted="2017-09-01T10:04:11.883" clientapp="..." hostname="..." hostpid="7980" loginname="..." isolationlevel="read committed (2)" xactid="265839001" currentdb="22" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
                <executionStack>
                    <frame procname="EXEC SPROC2" line="141" stmtstart="9324" stmtend="10424" sqlhandle="0x03001600a8150d4e1c673f01d0a700000100000000000000">
                        UPDATE BP
                        SET BP.[Status] = CASE @Cancel WHEN 1 THEN &apos;Voided&apos; ELSE &apos;Submitted&apos; END, BP.[MW] = CASE @Cancel WHEN 1 THEN 0 ELSE BP.[MW] END, BP.[SubmissionUserID] = @SubmissionUserID, BP.[SubmissionTime] = @SubmissionTime
                        FROM #FinalResult FR
                        CROSS JOIN Hour25 H25
                        INNER JOIN DeadLockTable  BP ON BP.[ID] = FR.[ID] AND BP.[Hour] = H25.[HE]
                        INNER JOIN fnc_List2Table(@HourList, &apos;,&apos;) HL ON H25.[HE] = HL.[Value] OR @HourList = &apos;0&apos;
                        WHERE (FR.[MarketType] = 1 OR (FR.[MarketType] = 2 AND BP.[Hour] = FR.[Hour]))
                    </frame>
                    <frame procname="adhoc" line="2" stmtstart="18" sqlhandle="0x010016002c9cbb0aa05380f3000000000000000000000000">
                        EXEC SPROC2
                    </frame>
                </executionStack>
                <inputbuf>
                    EXEC SPROC2
                </inputbuf>
            </process>
        </process-list>
        <resource-list>
            <keylock hobtid="72057594344112128" dbid="22" objectname="DealockTable" indexname="PK_DealockTable" id="lock451ea9e80" mode="X" associatedObjectId="72057594344112128">
                <owner-list>
                    <owner id="process5e45b88" mode="X"/>
                </owner-list>
                <waiter-list>
                    <waiter id="process5e13b88" mode="RangeS-S" requestType="wait"/>
                </waiter-list>
            </keylock>
            <keylock hobtid="72057594344112128" dbid="22" objectname="DealockTable" indexname="PK_DealockTable" id="lock109ed6380" mode="RangeS-U" associatedObjectId="72057594344112128">
                <owner-list>
                    <owner id="process5e13b88" mode="RangeS-S"/>
                </owner-list>
                <waiter-list>
                    <waiter id="process5e45b88" mode="X" requestType="convert"/>
                </waiter-list>
            </keylock>
        </resource-list>
    </deadlock>
</deadlock-list>

死锁图

【问题讨论】:

  • 是否需要在serializable事务隔离中运行选择查询?这可能是为什么您的选择查询试图获得更高级别的锁
  • @Xedni 这是一个金融应用程序,因此可序列化对我们来说几乎是不可避免的。我也会交叉发帖到 dba.stackexchange,谢谢你的建议。
  • 只是需要考虑的事情,因为这将有助于锁定和限制您的选择。您可以考虑在两个进程中使用sp_getapplock(在类似C# 中的行为更像lock)。这将至少阻止您添加的任何程序来尝试锁定您放入 applock'd 体内的相同内容。像快照隔离之类的东西也可能需要研究。

标签: sql sql-server sql-server-2008


【解决方案1】:

当一个进程(受害者)读取serializable 级别而另一个进程更新同一个聚簇表时,这是典型的死锁。

你对转换成什么感到困惑。

第一个进程(“process5e13b88”)获得RangeS-S锁并等待另一个RangS-S锁(这里有更新进程的X锁),它什么也不转换。

第二个“process5e45b88”在一个键上获得了X 锁定,在另一个键上获得了U,并希望将其转换为X,但它不能,因为有RangeS-S 锁定。

我附上一张图片,你可以看到一个进程只需要并获取RangeS-S 锁,而另一个只需要并获取X 锁。

当您看到 RangeS-U 锁定时,这意味着该范围上有 RangeS-S 锁定,但密钥本身具有 U 锁定

【讨论】:

  • 您能提出解决方案吗?我不想进行脏读,我无法可靠地重现该问题来测试任何可能改善情况的锁定提示。
【解决方案2】:

尝试在死锁表上创建非聚集索引,使用连接列作为键列,返回的(即 SELECT 部分中的列)列作为包含列。

阅读Using a Clustered Index to Solve a SQL Server Deadlock Issue 以获得进一步的解释。我知道标题听起来不像我要告诉你的,但这篇文章展示了如何使用非聚集索引来解决死锁问题。

【讨论】:

    猜你喜欢
    • 2013-01-25
    • 1970-01-01
    • 2011-11-01
    • 1970-01-01
    • 2019-02-23
    • 1970-01-01
    • 2015-06-01
    • 1970-01-01
    相关资源
    最近更新 更多