3

SQL Server 2005 を使用する .NET アプリケーションのデッドロックのトラブルシューティングを手伝っています。以下のトレースからの XML データがあります。

私を本当に困惑させているのはPK_Exp_Experience_PriorFirm、トランザクション分離レベルが読み取りコミットされている場合の RangeX-X ロックです。

私が読んだことはすべて、トランザクション分離レベル「シリアル化可能」を使用しているキー範囲ロックのみを取得することを示しています。これまでのところ、分離レベルを read commit 以外に設定しているアプリケーションの場所を見つけることができません。また、以下の XML は、read commit を使用していることを示しています。

しかし、read-committed を使用している場合、キー範囲ロックが存在することをトレースがどのように示しているかわかりません。誰もがそれがどのように起こっているのかについて考えを持っていますか?

<deadlock-list>
  <deadlock victim="processc2f438">
    <process-list>
      <process id="processc2f438" taskpriority="0" logused="13488" waitresource="KEY: 120:72057594583646208 (8201498b6efe)" waittime="484" ownerId="693258089" transactionname="user_transaction" lasttranstarted="2009-01-06T16:33:27.817" XDES="0xa71ce370" lockMode="U" schedulerid="1" kpid="9112" status="suspended" spid="53" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-01-06T16:33:27.863" lastbatchcompleted="2009-01-06T16:33:27.863" clientapp=".Net SqlClient Data Provider" hostname="CHQAPT3" hostpid="6464" loginname="AppUser" isolationlevel="read committed (2)" xactid="693258089" currentdb="120" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="adhoc" line="1" stmtstart="108" sqlhandle="0x0200000015d9962978fc6206b09e4c872150511b455e8923">
            UPDATE Exp_Experience_PriorFirm SET RelatedGuid = @newGuid WHERE RelatedGuid = @oldGuid
          </frame>
          <frame procname="mssqlsystemresource.sys.sp_executesql" line="1" sqlhandle="0x0400ff7fbe80662601000000000000000000000000000000">
            sp_executesql
          </frame>
          <frame procname="MyDb.dbo.Contact_MergeRelationships" line="74" stmtstart="4754" stmtend="4976" sqlhandle="0x0300780036a608461ed8af00669b00000100000000000000">
            EXEC sp_executesql @sql,
            N'@oldGuid uniqueidentifier, @newGuid uniqueidentifier',
            @oldGuid, @newGuid
          </frame>
          <frame procname="MyDb.dbo.Contact_Company_MergeRelationships" line="8" stmtstart="312" sqlhandle="0x03007800b271a129c8ccaf00669b00000100000000000000">
            EXEC Contact_MergeRelationships @oldGuid, @newGuid, 'Contact_Company', @excludedTableNames
          </frame>
        </executionStack>
        <inputbuf>
          Proc [Database Id = 120 Object Id = 698446258]
        </inputbuf>
      </process>
      <process id="processeb5d68" taskpriority="0" logused="14212" waitresource="KEY: 120:72057594594066432 (7c02a3a5890e)" waittime="2312" ownerId="693243114" transactionname="user_transaction" lasttranstarted="2009-01-06T16:33:20.957" XDES="0x8cdb9450" lockMode="S" schedulerid="2" kpid="9000" status="suspended" spid="73" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2009-01-06T16:33:29.770" lastbatchcompleted="2009-01-06T16:33:29.770" clientapp=".Net SqlClient Data Provider" hostname="CHQAPT3" hostpid="6464" loginname="AppUser" isolationlevel="read committed (2)" xactid="693243114" currentdb="120" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
        <executionStack>
          <frame procname="MyDb.dbo.Contact_Company_Delete" line="27" stmtstart="1128" sqlhandle="0x03007800b0e5761877cbaf00669b00000100000000000000">
            DELETE FROM Contact WHERE GUID = @Guid;
          </frame>
        </executionStack>
        <inputbuf>
          Proc [Database Id = 120 Object Id = 410445232]
        </inputbuf>
      </process>
    </process-list>
    <resource-list>
      <keylock hobtid="72057594583646208" dbid="120" objectname="MyDb.dbo.Exp_Experience_PriorFirm" indexname="PK_Exp_Experience_PriorFirm" id="lockd1d43f80" mode="RangeX-X" associatedObjectId="72057594583646208">
        <owner-list>
          <owner id="processeb5d68" mode="RangeX-X"/>
        </owner-list>
        <waiter-list>
          <waiter id="processc2f438" mode="U" requestType="wait"/>
        </waiter-list>
      </keylock>
      <keylock hobtid="72057594594066432" dbid="120" objectname="MyDb.dbo.Contact_PersonCompanyLocation" indexname="PK_Contact_PersonCompanyLocation" id="lockd20c4380" mode="X" associatedObjectId="72057594594066432">
        <owner-list>
          <owner id="processc2f438" mode="X"/>
        </owner-list>
        <waiter-list>
          <waiter id="processeb5d68" mode="S" requestType="wait"/>
        </waiter-list>
      </keylock>
    </resource-list>
  </deadlock>
</deadlock-list>
4

4 に答える 4

8

PK_Exp_Experience_PriorFirm の RangeX-X ロックは、カスケード削除の一部として取得されていました。

SQL Server は、カスケード削除などの特定の操作について、分離レベルを自動的にシリアル化可能にアップグレードします。

これについては、こちらで詳しく説明しています: Conor vs. Isolation Level Upgrade on UPDATE/DELETE Cascading RI

于 2009-03-20T09:54:59.440 に答える
4

期待どおり、READCOMMITTEDを使用しています。

このようなデッドロックは、UPDATEがクラスター化インデックスの排他的キーロックを取得して行を変更し、そのロックがクラスター化インデックスでのSELECTのブックマークルックアップをブロックする場合に発生する可能性があります。

この性質のロックは、多くの場合、対象となる非クラスター化インデックスを作成することで排除できます。

使用可能な別のオプションはREAD_COMMITTED_SNAPSHOT ON、データベースに設定することです。これにより、SELECTステートメントがコミットされたデータを読み取る方法が変わります。共有ロックを取得する代わりに、SELECTステートメントの開始時に開始されたトランザクションによって変更されたデータの以前のバージョン(スナップショット)を読み取ります。ただし、これは完全に無料ではありません。コストはでの活動の増加tempDBです。[READCOMMITTEDSNAPSHOTモードのトリガーで問題が発生する可能性もあります。]

于 2009-01-15T14:50:13.190 に答える
2

これに関するもう 1 つの一般的な説明は、IGNORE_DUP_KEY オプションが ON に設定された UNIQUE インデックスに関連している可能性があります。

From BOL - このオプションは、挿入操作が一意のインデックスに重複するキー値を挿入しようとしたときのエラー応答を指定します。IGNORE_DUP_KEY オプションは、インデックスが作成または再構築された後の挿入操作にのみ適用されます。このオプションは、CREATE INDEX、ALTER INDEX、または UPDATE の実行時には効果がありません。デフォルトはオフです。

ON 一意のインデックスに重複するキー値が挿入されると、警告メッセージが表示されます。一意性制約に違反する行のみが失敗します。

OFF 重複するキー値が一意のインデックスに挿入されると、エラー メッセージが表示されます。INSERT 操作全体がロールバックされます。

ここで言及されていないのは、このオプションが有効になっている場合、INSERTS 中に SERIALIZABLE 分離が強制されるということです。私は個人的に、挿入された行の一部が破棄される可能性があり、他に何もない可能性があるため、そうするための固有の要件を把握していませんが、それが何であるかです。SQL 開発チームをキューに入れて、ここに連絡してください...

この動作は簡単に実証できます。

最初に、典型的な PK を使用して新しいテーブルを作成します。

CREATE TABLE [dbo].[Test_RC_TIL_RangeLocks](
    [RID] [int] IDENTITY(1,1) NOT NULL,
    [Col1] [int] NOT NULL,
    [Col2] [int] NOT NULL,
    [Col3] [int] NOT NULL
) ON [PRIMARY]

次に IGNORE_DUP_KEY を ON にして、Col1 と Col2 に UNIQUE インデックスを追加します。

CREATE UNIQUE NONCLUSTERED INDEX [UIX_Test_RC_TIL_RangeLocks] ON [dbo].[Test_RC_TIL_RangeLocks](
    [Col1] ASC,
    [Col2] ASC
)WITH (
    PAD_INDEX  = OFF, 
    STATISTICS_NORECOMPUTE  = OFF, 
    SORT_IN_TEMPDB = OFF, 
    IGNORE_DUP_KEY = ON, --<<**THE OFFENDER**>>
    DROP_EXISTING = OFF, 
    ONLINE = OFF, 
    ALLOW_ROW_LOCKS  = ON, 
    ALLOW_PAGE_LOCKS  = ON) 
ON [PRIMARY]

次に、5 つの行を追加して、何が起こるか見てみましょう...

SET TRANSACTION ISOLATION LEVEL READ COMMITTED
DECLARE @C int
SELECT @C=5
BEGIN TRANSACTION
WHILE @C>0
BEGIN
    INSERT Test_RC_TIL_RangeLocks(Col1,Col2,Col3)
    VALUES (@C,@C+1,2*@C + 100)
    SET @C=@C-1
END
SELECT * FROM Test_RC_TIL_RangeLocks
EXEC sp_lock @@SPID
COMMIT

予想どおり、5 つの行が追加されました。

RID Col1    Col2    Col3
1   5       6       110
2   4       5       108
3   3       4       106
4   2       3       104
5   1       2       102

そして、私たちが興味を持っているロックは次のとおりです。

sid ObjId     IndId Type Resource                  Mode
53  0             0 DB                             S
53  0             0 DB                             S
53  402100473     1 KEY (8194443284a0)             X
53  402100473     2 KEY (550e0a2a4b96)             RangeX-X
53  402100473     2 KEY (ffffffffffff)             RangeS-U
53  1131151075    0 TAB                            IS
53  402100473     1 PAG 1:744                      IX
53  402100473     2 PAG 1:748                      IX
53  402100473     1 KEY (98ec012aa510)             X
53  402100473     1 KEY (a0c936a3c965)             X
53  402100473     2 KEY (ec04ac4bee1f)             RangeX-X
53  402100473     0 TAB                            IX
53  402100473     2 KEY (0207a0a08e23)             RangeX-X
53  402100473     2 KEY (7112ec63c430)             RangeX-X
53  402100473     1 KEY (59855d342c69)             X
53  402100473     1 KEY (61a06abd401c)             X
53  338100245     0 TAB                            IX

ああ、単純な挿入で READ COMMITTED 分離レベルの下で呼び出される恐ろしい SERIALIZABLE キー範囲ロック!

だからBOLが言うとき; キー範囲のロックが発生する前に、次の条件を満たす必要があります。

• トランザクション分離レベルは SERIALIZABLE に設定する必要があります。

それは常に正しいとは限らないことを覚えておいてください...

ps 設計上の注意として、一意の行値を持つ可能性のある重複キーをやみくもに削除することは、一般的に悪い習慣です。より良い方法は、INSERT ステートメントの一部として重複キーを挿入しようとしていないことを確認することです...

乾杯...

于 2012-11-15T05:13:31.313 に答える
0

SQL Docsによると、どういうわけか、トランザクション(または他のトランザクション)はシリアル化可能なレベルで実行されていました。

ここでキー範囲ロックのドキュメント。最初の文は次のとおりです。

キー範囲ロックは、シリアル化可能なトランザクション分離レベルを使用しているときに、Transact-SQLステートメントによって読み取られるレコードセットに暗黙的に含まれる行の範囲を保護します。

于 2009-01-15T14:47:50.917 に答える