web-dev-qa-db-ja.com

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

私はいくつかの再発するデッドロックに直面しています。そのうちの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なしでも発生しますか?
11
Magier

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

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

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

これはmightです。ロックされるリソースが少なくなり、操作がより速く完了する傾向があるためです。それが役立つ場合は、デッドロックを削減できますが、ほとんどの場合、それらを排除することはできません(ただし、読んでください)。

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

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

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

No、選択が実際にread uncommitted分離で実行される場合、互換性のないロックは取得されず、保持されないため別の順序。

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

助言

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

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

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

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

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

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

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

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

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

2
Leo Miller