如何解决索引/密钥相关的死锁问题

时间:2012-08-03 17:49:23

标签: sql-server sql-server-2008 tsql concurrency database-deadlocks

我使用deadlock graph in SQL Server 2008在我的SQL Server中诊断出死锁问题。

问题与我的索引有关。我有两个查询:一个长时间运行的报告,其中有很多连接和子查询,它们根据基表上的两个不同日期提取数据,还有一个快速更新查询,它会更新该基表上的相同日期。我有两个索引,并且报告想要对它们两者进行共享KEY锁定,而更新查询需要对它们两者进行独占KEY锁定,并且不知何故每个查询只设法获取其中一个键,因此两者都不能继续。

我该怎么做才能解决这个问题?

以下是我的具体情况:

我的基表如下所示:

CREATE TABLE job_tb{
    job_id int IDENTITY(1,1),
    createDate datetime NULL,
    upDate datetime NULL,
    dataField1 nchar(1),
    dataField2 nchar(2),
    --etc...
}

我的指数看起来像这样:

CREATE NONCLUSTERED INDEX idx_createDate ON job_tb(
  createDate DESC
)
INCLUDE(dataField1, dataField2)

CREATE NONCLUSTERED INDEX idx_upDate ON job_tb(
  upDate DESC
)
INCLUDE(dataField1, dataField2)

最后,我的更新如下所示:

BEGIN TRANSACTION;
    UPDATE job_tb
    SET
        dataField1 = @data
        upDate = @upDate
    WHERE
        job_id = @job_id
COMMIT TRANSACTION;

报告按日期计算各种统计数据,因此我不会在此处列出。我故意将idx_createDate和idx_upDate设计为“覆盖”或包含dataField1,因为它在该报告中大量使用。

我相信报告会抓取其中一个索引的共享锁,然后命中子查询并请求锁定第二个索引。同时,更新查询需要对两个索引进行独占锁定,以便更新upDate和包含的dataField1。

你们有什么想法?

修改 根据要求,这是XML死锁图:

<deadlock-list>  <deadlock>
<victim-list>
    <victimProcess id="processcf65288"/>
</victim-list>
<process-list>
    <process id="processcf65288" taskpriority="0" logused="0" waitresource="KEY: 6:72057597970874368 (eee1799e706c)" waittime="122" ownerId="421742704" transactionname="SELECT" lasttranstarted="2012-08-03T05:37:21.257" XDES="0x8611e8800" lockMode="S" schedulerid="50" kpid="8560" status="suspended" spid="70" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2012-08-03T05:37:21.257" lastbatchcompleted="2012-08-03T05:37:21.257" clientapp="Internet Information Services" hostname="xxx" hostpid="11964" loginname="xxx" isolationlevel="read committed (2)" xactid="421742704" currentdb="6" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
            <frame procname="" line="28" stmtstart="1276" stmtend="4826" sqlhandle="0x03000600311ac36c65a31701a1a000000100000000000000">
            </frame>
            <frame procname="" line="1" sqlhandle="0x01000600f61bee3600932ae3090000000000000000000000">
            </frame>
        </executionStack>
        <inputbuf> exec MonthlyReport @id = 41
        </inputbuf>
    </process>
    <process id="processd2b6bc8" taskpriority="0" logused="1908" waitresource="KEY: 6:72057597970939904 (8e8117a49479)" waittime="2242" ownerId="421742551" transactionname="user_transaction" lasttranstarted="2012-08-03T05:37:20.447" XDES="0x7e84ad0a0" lockMode="X" schedulerid="63" kpid="12700" status="suspended" spid="89" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2012-08-03T05:37:20.443" lastbatchcompleted="2012-08-03T05:37:20.443" clientapp="Internet Information Services" hostname="xxx" hostpid="11964" loginname="xxx" isolationlevel="read committed (2)" xactid="421742551" currentdb="6" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
        <executionStack>
            <frame procname="" line="47" stmtstart="2342" stmtend="2640" sqlhandle="0x03000600e7dd9c717cbbb900ec9f00000100000000000000">
            </frame>
            <frame procname="" line="1" sqlhandle="0x01000600311d7a152032f9be040000000000000000000000">
            </frame>
        </executionStack>
        <inputbuf> exec UpdateJob @dataField1 = &apos;C&apos;, @upDate = &apos;8/3/2012 5:37:20 AM&apos;, @job_id = 1542687
        </inputbuf>
    </process>
</process-list>
<resource-list>
    <keylock hobtid="72057597970874368" dbid="6" objectname="" indexname="" id="lock612859900" mode="X" associatedObjectId="72057597970874368">
        <owner-list>
            <owner id="processd2b6bc8" mode="X"/>
        </owner-list>
        <waiter-list>
            <waiter id="processcf65288" mode="S" requestType="wait"/>
        </waiter-list>
    </keylock>
    <keylock hobtid="72057597970939904" dbid="6" objectname="" indexname="" id="lock612a15300" mode="S" associatedObjectId="72057597970939904">
        <owner-list>
            <owner id="processcf65288" mode="S"/>
        </owner-list>
        <waiter-list>
            <waiter id="processd2b6bc8" mode="X" requestType="wait"/>
        </waiter-list>
    </keylock>
</resource-list>  
</deadlock> /deadlock-list> 

1 个答案:

答案 0 :(得分:5)

基于注释中的Q / A讨论并在分析死锁图之后,这是当前两个索引未完全覆盖报表查询的情况。该报告将首先开始查看非聚簇索引。它找不到所有需要的信息。因此,它在主表上执行键查找以获取剩余数据。 但更新的工作正好相反。更新将首先锁定主表,并更新它的数据,然后转到所有索引并更新它们。因此陷入僵局。

解决此问题的一种方法是按索引覆盖整个报表查询。但这会导致更新变得缓慢。

其他解决方案是将报表查询分解为两个,并使用临时表变量从索引中收集数据,然后执行键查找。注意报告查询不应在可序列化事务模式下运行。否则,事务不会释放它刚刚读取的读锁。

希望我很清楚。如果您有任何疑问,请告诉我。