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>

2 番目のプロセス:

   <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) があります。削除するとページ ロックが発生するのはなぜですか?何が起こっているのか理解するのを手伝ってもらえますか?同じクライアントからクエリを送信しますが、それは不可能です.これらは、同じ Web ページにアクセスする異なるクライアントである必要があります.

実際、最初の質問に対する答えはわかっていると思います。範囲を削除するにはページ ロックが必要です。しかし、どうすればこれを回避できますか?

インデックス クエリの結果:

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

updatedeleteの実行計画。

4

2 に答える 2

2

回答が更新されました

観察

この図は、XML デッドロック グラフに基づいています。 ここに画像の説明を入力

これは、spid52 が pageid=190354 (UserPlanOption テーブル) で IX ロックを保持しており、(同じ UserPlanOption テーブルから) pageid=190487 で IU ロックを要求していることを示しています。UserPlanOption テーブルはヒープ テーブルだと思います。つまり、クラスター化されたインデックスがありません。また、これは、PK がクラスター化されていないことを意味します。このクエリを実行する場合:

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

UserPlanOption テーブルからすべてのインデックスを含むリストを取得します (このリストを公開できますか?)。この場合、spid52 は UPDATE を実行するため、両方のロック (IU と IX) は、(私の観点から) UPDATE 実行計画で可能なテーブル/インデックス スキャン演算子を示します。

ただし、spid57 は同じリソース (pageid=190487) に既に U ロックを持っています。同じ接続 (spid57) が別のページ (pageid=190354) で別の U ロックを要求しますが、このリソース (ページ) は spid52 (IX) によって既にロックされています。

理由 (ロックの互換性マトリックスを参照):

[i] 既存の IX および要求された U ロックまたは

[ii] 既存の U および要求された IU ロック

互換性がありません。適切デッドロックがあります。

DELETE ステートメントのキャッシュされたプランは次のとおりです。 ここに画像の説明を入力

ノート:

  • ここで、クラスター化インデックス スキャン (並列処理を使用) 演算子により、DBMS が UserPlanOption テーブルからすべての行をスキャンするように強制されます。

  • 行の推定数はわずか 5 (削除された行の推定数) であり、*Parallelism演算子の存在は、UserPlanOption テーブルが大きいことを示しています。

  • SQL Server からのインデックスの提案を確認できます。

UPDATE ステートメントのキャッシュされたプランは次のとおりです。 ここに画像の説明を入力

この計画の主な問題は次のとおりです。Rate テーブルでのクラスター化インデックス スキャン、[N][VAR]CHAR(?) からへの暗黙的な変換を伴う Compute Scalar、およびUser テーブルを使用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 ステートメントのパフォーマンスを向上させるために、提案されたインデックスを作成できます。

-- 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 テーブルのすべての行に対してこの関数が呼び出されると、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です。この解決策は、最後の解決策にすぎません。

于 2012-09-29T17:33:36.507 に答える
1

デッドロックの理由は、UserPlanOptionテーブルが異なる順序でアクセスされるという事実によるものと思われます。おそらく、FamilyTierId列にインデックスが存在するためです。

この種の問題について詳しくは、こちらこちらをご覧ください。

このデッドロック状態を回避する1つの可能な方法は、UPdateステートメントの前のUserID列に基づいて事前にロックを取得することです。

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

于 2012-09-29T00:08:40.837 に答える