Tablockヒントがデッドロックをトリガーする


10

最小限のログを使用して、2つのデータセットを空のヒープテーブルに挿入しました。これは、次の形式のSQLで並列実行されている2つのSQL実行タスクを使用して行われました。

INSERT INTO Table (TABLOCK) SELECT FROM ...

ジョブが少しハングした後、SQLタスクの1つがデッドロックの犠牲になりました。以下は、デッドロックグラフのXML出力です。

誰かが内部で何が起こっていたか説明できますか?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

ほとんどの場合、2つのSQL実行タスクが正常に並行して実行できることがわかったので、事態はかなり複雑になります。以下をお試しください:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

唯一の違いはSELECT ... FROM ...ステートメントであるため、SELECT ... FROM ...ステートメントがロックモードに影響を与える可能性があるように見えますか?


デッドロックを防ぐために、TABLOCKの代わりにTABLOCKXを指定できます。これにより、テーブルへのアクセスもシリアル化されますが、最小限のログが記録されます。
Dan Guzman、

回答:


8

データのロードパフォーマンスガイドは、 SQL Server 2008のために書かれましたが、私の知る限り、マイクロソフトは、ヒープのために、この領域での任意の改善を行っていません。これは、ロードシナリオの見積もりです。

空の分割されていないテーブルの一括読み込み

分割されていないテーブルへのデータのロードは、簡単な操作ですが、いくつかの方法で最適化できます。

...

ヒープに対する複数の同時挿入操作は、選択された一括メソッドがテーブルに対して一括更新(BU)ロックを発行する場合にのみ可能です。2つの一括更新(BU)ロックは互換性があるため、2つの一括操作を同時に実行できます。

このシナリオでは、INSERT…SELECTとSELECT INTOの両方に欠点があります。これらの操作は両方とも、宛先に対して排他(X)のテーブルレベルロックを取得します。つまり、一度に実行できる一括読み込み操作は1つだけであり、スケーラビリティが制限されます。ただし、TABLOCKヒントを指定した場合、BCP、BULK INSERT、およびIntegration Servicesはすべて、一括更新(BU)ロックを取得できます。

重要な部分は、でBUロックを取得しないことですINSERT ... SELECT。テーブルには常に排他ロックがかかるためINSERT、一度に実行できるのは1つだけです。

コメントで、100k行以下を挿入し、挿入中に他のプロセスがテーブルで実行されないことを述べました。2つのINSERTクエリをデータベースに送信すると、次の3つのうちの1つが発生すると予想されます。

  1. 1つの挿入が最初に実行され、他の挿入をブロックします。2番目の挿入は、最初の挿入が完了するまで待機します。
  2. 1つの挿入は、2番目の挿入が始まる前に終了します。明示的なブロッキングはありませんが、同時に実行されることはありません。
  3. デッドロックが発生し、1つの挿入のみが正常に完了します。

いずれの場合もTABLOCKX、クエリにヒントを追加することでメリットが得られるか、害を受けないので、デッドロックを回避することをお勧めします。デッドロックが発生することがある理由を知りたい場合は、別の答えを探す必要があります。

並列挿入が本当に必要な別のシナリオでは、BUの問題を回避する2つの方法は、ヒープをパーティション分割して、各セッションを個別のパーティションに挿入するか、BCP、BULK INSERT、または統合サービスを介してデータをロードすることです。 。


回答ありがとうございます。デッドロックに遭遇したのは、今のところ唯一のケースです。ほとんどの場合、INSERT INTO WITH(TABLOCK)SELECT FROMを並行して発行すると、ジョブは失敗しません。ところで、私はSQL SERVER 2008 R2を使用しています。成功例を質問に追加しました。
SqlWhale

失敗しない場合の@tecは、おそらく実際にはシリアルで実行されることになります。たとえば、プランのコンパイルなど、特定の起動時間がある場合にのみ問題が発生することがあります。
マーティン・スミス

@MartinSmithプロジェクトで問題が発生したクエリは、SELECT 1の例よりもはるかに複雑です。これは、より多くのコンパイルオーバーヘッドを必要としますが、いくつかのテーブルから読み取るだけです。より複雑なクエリでこのタイプのデッドロックを再現しようとしています。
SqlWhale

@TecKnowNothing行をいくつ挿入しますか?プロセスは1日に何回実行されますか?データのロード中に、他のクエリがテーブルからSELECTを実行しますか?
ジョー・オブビッシュ

@JoeObbish 1.ここでは行数の問題はないと思います。クエリごとに4000〜70000しかありませんが、それらはキューブで使用するための高度に集計されたデータです。2.まだテスト段階にあり、1日に1回実行されることになっています。3.ターゲットテーブルから他に何も読み取っていません。
SqlWhale

4

あなたはdbo.TargetTable2つのセッションから挿入し、両方ともTABLOCKヒント process9609dc8を使用しています。両方のプロセスと同時に保持できるように互いに互換性のあるprocess5e13048プロセスの保持Sch-SIXロック。しかし、どちらもIXロックをExclusive Xタイプに変換したいと考えています。Xロックは互いに互換性がありません。したがって、SQLサーバーは、互いに無限に待機するのではなく、セッションの1つをデッドロックの犠牲者として選択しました。

基本的なデッドロック情報。

ロックの互換性(データベースエンジン)のグラフ。

デッドロックの検出と終了。

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