0

第一道工序:

<deadlock-list>
 <deadlock victim="process8d9798">
  <process-list>
   <process id="process8d9798" taskpriority="0" logused="0" waitresource="PAGE:
    5:1:190354" waittime="3203" ownerId="53807810" transactionname="DELETE" 
    lasttranstarted="11:29:29.153" XDES="0x3dbb518" lockMode="U" 
    schedulerid="2" kpid="1792" status="suspended" spid="57" sbid="0" ecid="1" 
    priority="0" transcount="0" lastbatchstarted="2012-09-28T11:29:29.120" 
    lastbatchcompleted="11:29:29.120" clientapp=".Net SqlClient Data Provider" 
    hostname="xxx" hostpid="4460" isolationlevel="read uncommitted (1)" 
    xactid="53807810" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" 
    clientoption2="128056">
    <executionStack>
     <frame procname="Chooser2.dbo.DeleteUserSelections" line="15" stmtstart="360"
     stmtend="464" sqlhandle="0x030005008839117bf599a500099800000100000000000000">
   DELETE UserPlanOption
    WHERE UserID = @userId     </frame>
    </executionStack>
    <inputbuf>
    </inputbuf>
   </process>

第二道工序:

   <process id="processb84988" taskpriority="0" logused="1744" waitresource="PAGE:
    5:1:190487" waittime="3203" ownerId="53807415" transactionname="user_transaction" 
   lasttranstarted="11:29:13.513" XDES="0x2fc4e6e0" lockMode="IU" 
   schedulerid="4" kpid="4628" status="suspended" spid="52" sbid="0" ecid="0" 
   priority="0" transcount="2" lastbatchstarted="11:29:13.513" 
   lastbatchcompleted="11:29:13.513" 
   clientapp=".Net SqlClient Data Provider" hostname="xxx" 
   hostpid="4460" loginname="chooserpd" isolationlevel="read uncommitted (1)" 
   xactid="53807415" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" 
   clientoption2="128056">
    <executionStack>
     <frame procname="Eligibility" line="149" stmtstart="10566" 
   stmtend="11604" sqlhandle="0x03000500171b4f52c1a6e200ada000000100000000000000">
UPDATE  UserPlanOption 
    SET     RateID = r.ID
    FROM    [User] u WITH (NOLOCK)
        LEFT JOIN Rate r ON r.FamilyTierID = u.FamilyTierID 
    WHERE   UserPlanOption.PlanOptionID NOT IN (SELECT ppo.PlanOptionID FROM 
                    @PORACPlanOptions ppo) AND
        u.ID = @userID AND u.ID = UserPlanOption.UserID AND
        r.PlanOptionID = UserPlanOption.PlanOptionID AND
        r.Criterion1 = dbo.GetPlanOptionAreaID_36()
      </frame>
    </executionStack>
    <inputbuf>
Proc [Database Id = 5 Object Id = 1380915991]    </inputbuf>
   </process>
  </process-list>

资源清单:

  <resource-list>
   <pagelock fileid="1" pageid="190354" dbid="5" objectname="UserPlanOption" 
   id="lock1e482d80" mode="IX" associatedObjectId="72057594060996608">
    <owner-list>
     <owner id="processb84988" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process8d9798" mode="U" requestType="wait"/>
    </waiter-list>
   </pagelock>
   <pagelock fileid="1" pageid="190487" dbid="5" objectname="UserPlanOption" 
   id="lock25b32a80" mode="U" associatedObjectId="72057594060996608">
    <owner-list>
     <owner id="process8d9798" mode="U"/>
    </owner-list>
    <waiter-list>
     <waiter id="processb84988" mode="IU" requestType="wait"/>
    </waiter-list>
   </pagelock>
  </resource-list>
 </deadlock>
</deadlock-list>

表“UserPlanOption 有一个复合 PK(UserId 和 PlanOptionId)。为什么删除会导致页面锁定?有人可以帮我理解这是怎么回事吗?让我困惑的是,我认为死锁是由后续引起的来自同一个客户端的查询,但这是不可能的。这些必须是访问同一个网页的不同客户端。

实际上,我想我知道第一个问题的答案——删除范围需要页面锁定。但我怎样才能解决这个问题?

索引查询的结果:

name                type type_desc is_unique data_space_id ignore_dup_key 
------------------- ---- --------- --------- ------------- -------------- 
PK_UserPlanOption_1 1    CLUSTERED 1         1             0              


is_primary_key is_unique_constraint fill_factor is_padded is_disabled 
-------------- -------------------- ----------- --------- ----------- 
1              0                    0           0         0           

is_hypothetical allow_row_locks allow_page_locks
--------------- --------------- ----------------
0               1               1

更新删除的执行计划。

4

2 回答 2

2

答案已更新

观察

此图基于 XML 死锁图: 在此处输入图像描述

它显示 spid52 在 pageid=190354(UserPlanOption 表)上具有 IX 锁,并且正在请求 pageid=190487 上的 IU 锁(来自同一个 UserPlanOption 表)。我认为 UserPlanOption 表是一个堆表,这意味着它没有聚集索引。此外,这意味着您的 PK 是非集群的。如果您将运行此查询:

SELECT i.*
FROM sys.indexes i
WHERE i.object_id = OBJECT_ID('UserPlanOption')

您将从 UserPlanOption 表中获得包含所有索引的列表(您可以发布此列表吗?)。在这种情况下,因为 spid52 执行 UPDATE,所以两个锁(IU 和 IX)都在 UPDATE 执行计划中显示(从我的角度来看)一个可能的表/索引扫描运算符。

但是,spid57 在同一资源上已经有一个 U 锁 (pageid=190487)。同一个连接 (spid57) 在另一个页面 (pageid=190354) 上请求另一个 U 锁,但此资源(页面)已被 spid52 (IX) 锁定。

因为(参见锁定兼容性矩阵):

[i] 现有的 IX 和请求的 U 锁或

[ii] 现有的 U 和请求的 IU 锁

不兼容你有一个很好的死锁。

DELETE 语句的缓存计划是 在此处输入图像描述

笔记:

  • 现在,聚集索引扫描(具有并行性)运算符强制 DBMS 扫描 UserPlanOption 表中的所有行,

  • 估计的行数只有 5(估计删除的行数) abd 存在*Parallelism运算符表明 UserPlanOption 表很大,

  • 您可以看到来自 SQL Server 的索引建议。

UPDATE 语句的缓存计划是 在此处输入图像描述

该计划的主要问题是:Rate 表上的聚簇索引扫描,一个具有从[N][VAR]CHAR(?) 到的隐式转换的计算标量,以及在 User 表INT之前的过滤器。JOIN

解决方案

基于这些观察,解决方案应该是:

[ 1 ]在 UserPlanOption(UserID,PlanOptionID) 上创建索引 IN_UserPlanOption_UserID_PlanOptionID;

-- SQL Server's suggestion
CREATE INDEX IN_UserPlanOption_UserID
ON UserPlanOption(UserID)
INCLUDE(PlanOptionID); -- optional

注意 1:在我的选项中,UserPlanOption 表 (DELETE) 上的聚集索引扫描是导致此死锁的主要原因。

注意 2:UserPlanOption 在(PlanOptionID, UserID)列上有一个聚集索引。此索引有助于 UPDATE 语句(请参阅 PK_UserPlanOption_1: 上的 Seek 运算符WHERE ... AND u.ID = UserPlanOption.UserID AND r.PlanOptionID = UserPlanOption.PlanOptionID AND ...),但不能帮助 DELETE 语句 ( WHERE UserID=@UserID)。

[ 2 ] 为了提高 UPDATE 语句的性能,您可以创建 sugested 索引:

-- SQL Server's suggestion
CREATE INDEX IN_Rate_FamilyTierID
ON dbo.Rate(FamilyTierID)
INCLUDE (PlanOptionID, Criterion1);

[ 3 ] 要删除隐式转换,您可以重写 DELETE 语句:

DECLARE @Criterion1 Criterion1_datatype? 
SET @Criterion1 = dbo.GetPlanOptionAreaID_36()

UPDATE  UserPlanOption 
    SET     RateID = r.ID
    FROM    [User] u 
        LEFT JOIN Rate r ON r.FamilyTierID = u.FamilyTierID 
    WHERE   UserPlanOption.PlanOptionID NOT IN (SELECT ppo.PlanOptionID FROM 
                    @PORACPlanOptions ppo) AND
        u.ID = @userID AND u.ID = UserPlanOption.UserID AND
        r.PlanOptionID = UserPlanOption.PlanOptionID AND
        r.Criterion1 = @Criterion1

原始源代码(UPDATE 语句)包含此过滤器r.Criterion = dbo.GetPlanOptionAreaID_36(...)。此时,如果对 Rate 表中的每一行都调用此函数,则计算机标量运算符可能是另一个性能问题。

这个函数是确定性函数吗?

SELECT  r.IS_DETERMINISTIC, r.*
FROM    INFORMATION_SCHEMA.ROUTINES r
WHERE   r.ROUTINE_NAME='GetPlanOptionAreaID_36'

[4] 我的建议是不要使用NOLOCK提示和/或SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED。这个解决方案应该只是最后一个解决方案

于 2012-09-29T17:33:36.507 回答
1

我相信死锁的原因是由于 UserPlanOption 表的访问顺序不同,很可能是由于 FamilyTierId 列上存在索引。

您可以在此处此处阅读有关此类问题的更多信息

避免这种死锁情况的一种可能方法是在 UPdate 语句之前根据 UserID 列预先获取锁,如

SELECT @userId = UserId 从 UserPlanOption WITH(UPDLOCK) WHERE UserID = @userId

于 2012-09-29T00:08:40.837 回答