第一个过程:
<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
答案 0 :(得分:2)
回答更新
此图表基于XML死锁图:
它显示spid52在pageid = 190354(UserPlanOption表)上有一个IX锁定,并在pageid = 190487(来自同一个UserPlanOption表)上请求IU锁定。 我认为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)请求另一个页面上的另一个U锁(pageid = 190354),但此资源(页面)已被spid52(IX)锁定。
因为(见Lock compatibility matrix):
[i]现有的IX和请求的U锁或
[ii]现有的U和请求的IU锁
不兼容你有一个漂亮的死锁。
DELETE语句的缓存计划是
注意:
现在,集群索引扫描(带并行性)操作符强制DBMS扫描UserPlanOption表中的所有行,
估计行数仅为5(估计删除的行数),并且存在 * Parallelism 运算符表明UserPlanOption表很大,
您可以从SQL Server中看到索引建议。
UPDATE语句的缓存计划是
此计划的主要问题是:群集上的群集索引扫描,具有从[N][VAR]CHAR
(?)到INT
的隐式转换的计算标量以及JOIN
之前的过滤器使用用户表。
基于这些观察,解决方案应该是:
[1] CREATE INDEX IN_UserPlanOption_UserID_PlanOptionID
ON 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(...)
。此时, if 从Rate表中为每一行调用此函数,那么Computer Scalar运算符可能是另一个性能问题。
这个功能是否具有确定性功能?
SELECT r.IS_DETERMINISTIC, r.*
FROM INFORMATION_SCHEMA.ROUTINES r
WHERE r.ROUTINE_NAME='GetPlanOptionAreaID_36'
[4]我的建议是不要使用NOLOCK
提示和/或SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
。此解决方案应该只是最后一个解决方案。
答案 1 :(得分:1)