SELECT…WITH XLOCKを使用する理由は何ですか?


11

私はいくつかの再発するデッドロックに直面しています。そのうちの1つはキーロックであり、デッドロックの犠牲になるXLOCKヒントを含むSELECTクエリが含まれています。もう1つのステートメントは、最初のクエリのビューの一部であるテーブルの1つへのINSERTです。

見る:

create view dbo.viewE
 as
    select * from dbo.E  
    where myValue > 13000 

クエリを選択:

select * from dbo.viewE with (XLOCK) where A > GETUTCDATE() 

INSERTステートメント:

INSERT INTO [dbo].[E] (myValue,A) VALUES (10,GetDate())

基になるテーブルdbo.Eは、約20列に約300万行を保持しています。それらの一部はntextです。

クエリを取り出し、2つのトランザクションで手動でシミュレーションすると、動作は再現可能です。XLOCKが選択から削除されると、動作が変わります。

デッドロックグラフ:

<deadlock-list>
 <deadlock victim="process222222221">
  <process-list>
   <process id="process222222221" taskpriority="0" logused="0" waitresource="KEY: 5:72057604035644444 (ccdf51accc0c)" waittime="2522" ownerId="27202256401" transactionname="SELECT" lasttranstarted="2015-09-14T16:32:36.160" XDES="0x2f1ec5ca0" lockMode="RangeX-X" schedulerid="15" kpid="12936" status="suspended" spid="359" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2015-09-14T16:32:36.160" lastbatchcompleted="2015-09-14T16:32:36.160" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="serializable (4)" xactid="27202256401" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="48" sqlhandle="0x02000000611e4523142b2318c47c87313a9b2ba587ff3130">
        SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()      </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>
(@UICulture nvarchar(5))SELECT * FROM viewE WITH (XLOCK) WHERE A &lt; GetUtcDate()    </inputbuf>
   </process>
   <process id="process6022222" taskpriority="0" logused="161152" waitresource="KEY: 5:72057604035644444 (cd874c2ba438)" waittime="1370" ownerId="27202248438" transactionguid="0x8de5ccd6eeef67469c6234af59e44ca5" transactionname="DTCXact" lasttranstarted="2015-09-14T16:32:34.767" XDES="0x4aa0bf950" lockMode="RangeI-N" schedulerid="14" kpid="6636" status="suspended" spid="329" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2015-09-14T16:32:37.300" lastbatchcompleted="2015-09-14T16:32:37.300" clientapp="x" hostname="x" hostpid="14536" loginname="x" isolationlevel="read uncommitted (1)" xactid="27202248438" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056">
    <executionStack>
     <frame procname="adhoc" line="1" stmtstart="936" sqlhandle="0x020000004853462f09790a4ddedc0d574c2afa539aef1c0e">
     INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
     </frame>
     <frame procname="unknown" line="1" sqlhandle="0x000000000000000000000000000000000000000000000000">
unknown     </frame>
    </executionStack>
    <inputbuf>INSERT INTO [E] ([a], [b], [c],...) VALUES (@aDate, @bDate, @c, ...)
    </inputbuf>
   </process>
  </process-list>
  <resource-list>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock258b6dc80" mode="X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process6022222" mode="X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process222222221" mode="RangeX-X" requestType="wait"/>
    </waiter-list>
   </keylock>
   <keylock hobtid="72057604035644444" dbid="5" objectname="db.dbo.E" indexname="IX_index1" id="lock7b145c400" mode="RangeX-X" associatedObjectId="72057604035644444">
    <owner-list>
     <owner id="process222222221" mode="RangeX-X"/>
    </owner-list>
    <waiter-list>
     <waiter id="process6022222" mode="RangeI-N" requestType="wait"/>
    </waiter-list>
   </keylock>
  </resource-list>
 </deadlock>
</deadlock-list>

これを理解している限り、必要な値を収集するために非クラスター化インデックスとクラスター化インデックスを使用する、カバーされていないインデックスクエリによって基本的に引き起こされるKEYLOCKデッドロックを見ています。

私の質問:

  1. 必要なNTEXT列が含まれているため、カバリングインデックスを作成できません。行数を大幅に減らすことは、ここで役立ちますか?
  2. SELECTがXLOCKで実行されることを知らないだけの理由はありますか?デッドロックはXLOCKなしでも発生しますか?

回答:


15

これを理解している限り、必要な値を収集するために非クラスター化インデックスとクラスター化インデックスを使用する、カバーされていないインデックスクエリによって基本的に引き起こされるKEYLOCKデッドロックを見ています。

本質的に、はい。読み取り操作(選択)は、最初に非クラスター化インデックスにアクセスし、次にクラスター化インデックス(検索)にアクセスします。書き込み操作(挿入)は、最初にクラスター化インデックスにアクセスし、次に非クラスター化インデックスにアクセスします。互換性のないロックを保持している別の順序で同じリソースにアクセスすると、デッドロックが発生する可能性があります。

行数を大幅に減らすことは、ここで役立ちますか?

それは可能性が少ないリソースがロックされているので、操作がより迅速に完了する傾向があります。効果がある場合はデッドロックを減らすことができますが、ほとんどの場合、それらを排除することはできません(ただし、読んでください)。

SELECTがXLOCKで実行されることを知らないだけの理由はありますか?

あんまり。このようなロックのヒントは、分離、ロック、デッドロックがどのように機能するかを完全に理解していない人が、問題を減らしたり解消したりするための必死の試みでしばしば導入されます。

デッドロックはXLOCKなしでも発生しますか?

いいえ、互換性のないロックは別の順序で取得(および保持)されないため、選択が実際にコミットされていない読み取り分離で実行される場合。

はいロック分離レベルが使用され、互換性のないロックが取得され、一貫性のない順序で保持されている場合(たとえば、非クラスター化で共有(S)、読み取り時にクラスター化でS)。このシナリオでデッドロックが発生する可能性は、取得されたロックの数と保持されている期間によって異なります。

助言

(レビューで)本当に際立っているのは、selectトランザクションがシリアライズ可能な分離で実行されていることです。フレームワークによって設定されているか、DTC(分散トランザクションコーディネーター)の使用が原因である可能性があります。デッドロックグラフのtransactionname = "DTCXact"を参照してください。この理由を調べ、可能であれば変更する必要があります。

このシリアライズ可能へのエスカレーションがなければ、XLOCKヒントが削除されていると仮定すると、このデッドロックが発生しない可能性が非常に高くなります。とは言っても、コミットされていない分離読み取りで読み取りを行うことになるため、一貫性の保証はほとんどありません。

アプリケーションとSQL Serverコードが行のバージョンの読み取りを許容できる場合、読み取りのコミットスナップショット分離(RCSI)またはスナップショット分離(SI)を読み取りに変更XLOCKすると、一貫性のあるポイントインを提示しながらデッドロックを回避(削除!)することもできます。コミットされたデータの時間ビュー。もちろんこれは、シリアライズ可能な分離を回避できることを前提としています。

最終的には、このXLOCKヒント非生産的ですが、シリアライズ可能な分離レベルを使用する理由を調べる必要があります。これtrancount = 2も興味深いです-おそらく、ここで意図せずにトランザクションをネストしています。チェックする他の何か。


2
  1. 行数を大幅に減らすと、デッドロックが発生する可能性は低くなりますが、完全になくなるわけではありません。

簡単に言うと、selectは最初にインデックスを使用して選択する行を決定し、次に行をフェッチし、挿入が行を挿入しているときに、(XLOCKED)インデックスを更新しようとします。

  1. アプリケーション開発者は、同じトランザクションで後でデータを更新したい場合、XLOCKを使用する傾向があります。これにより、誰もその下のデータを更新できなくなります。XLOCKが必要かどうかを確認するために、アプリケーションの動作を調査します。

そうは言っても、XLOCKを削除しても問題は解決しないでしょう。SELECTは引き続きインデックスの共有ロックを取得し、INSERTはXLOCKによる更新を要求します。共有ロックとXLOCKを一緒にオブジェクトに存在させることはできないため、デッドロックが発生します。IX_Index1は、MyValueまたはA、あるいはその両方でなければなりません。

このタイプのデッドロックは、設計が不十分なインデックスやインデックスが多すぎるために発生することがよくあります。または不十分に書かれたコード。最良のオプションは、selectを別のインデックスを使用するように書き換えられる方法があるかどうかを確認することです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.