次の例では、SELECT INTOクエリをラップする明示的なトランザクションブロックからどのような種類のロックが生成されますか?
BEGIN TRANSACTION T1
SELECT Field1, Field2, Field3
INTO TableB
FROM TableA
WHERE Field3 > Value1
COMMIT TRANSACTION T1
ノート:
1)TableAの行数は約1,000億ですが、フィルターによりクエリが返すのは約500万行だけです。
2)Field3はTableAのクラスター化インデックスであり、生成されたクエリプランはクラスター化インデックスシークを実行しています。
3)私のシナリオの分離レベルはデフォルト(コミットの読み取り)です。
このような明示的なトランザクションブロックは、クエリが明示的なトランザクションでラップされなかった場合に、テーブルに対してより多くのロック(またはロックのエスカレーションの可能性)を引き起こす可能性がありますか?
500万行は大量です。理論的には、どのような場合でも(明示的または暗黙的なトランザクション)TableAがテーブルレベルのロックに共有ロックをエスカレートします。
しかし、実験的にこれを見つけることができます。拡張イベントセッションをセットアップし、「lock_escalation」イベントを監視するだけです。
次に、明示的および暗黙的なトランザクションでクエリを実行し、XEセッションログをチェックして、lock_escalationイベントを確認します。
この特定の例では、_SELECT...INTO
_ステートメントが含まれているため、yesと言っても、ロックに違いがあります動作。
_SELECT...INTO
_ステートメントは「アトミックステートメント」ではありません 2つの部分で実行されます :新しいテーブルを作成してから、SELECT
およびINSERT
を実行しますクエリの一部。
少なくとも、明示的なトランザクションを備えたバージョンは、ターゲットテーブルの作成に関連するすべてのメタデータロックと、ターゲットテーブル自体のテーブルレベルのロックを、トランザクションが終了するまで保持します。
明示的なトランザクションでこのクエリのバージョンを実行した後の_EXEC master.dbo.sp_WhoIsActive @get_locks = 1;
_からの簡単な抜粋を次に示します。
_ <Object name="sysrowsets" schema_name="sys">
<Locks>
<Lock resource_type="KEY" index_name="clust" request_mode="X" request_status="GRANT" request_count="2" />
<Lock resource_type="OBJECT" request_mode="IX" request_status="GRANT" request_count="1" />
</Locks>
</Object>
<Object name="sysrscols" schema_name="sys">
<Locks>
<Lock resource_type="KEY" index_name="clst" request_mode="X" request_status="GRANT" request_count="6" />
<Lock resource_type="OBJECT" request_mode="IX" request_status="GRANT" request_count="1" />
</Locks>
</Object>
<Object name="sysschobjs" schema_name="sys">
<Locks>
<Lock resource_type="KEY" index_name="clst" request_mode="X" request_status="GRANT" request_count="2" />
<Lock resource_type="KEY" index_name="nc1" request_mode="X" request_status="GRANT" request_count="1" />
<Lock resource_type="KEY" index_name="nc2" request_mode="X" request_status="GRANT" request_count="1" />
<Lock resource_type="KEY" index_name="nc3" request_mode="X" request_status="GRANT" request_count="2" />
<Lock resource_type="OBJECT" request_mode="IX" request_status="GRANT" request_count="1" />
</Locks>
</Object>
<Object name="TableB" schema_name="dbo">
<Locks>
<Lock resource_type="ALLOCATION_UNIT.BULK_OPERATION_PAGE" request_mode="S" request_status="GRANT" request_count="1" />
<Lock resource_type="HOBT" request_mode="Sch-M" request_status="GRANT" request_count="1" />
<Lock resource_type="OBJECT" request_mode="Sch-M" request_status="GRANT" request_count="1" />
<Lock resource_type="PAGE" page_type="*" request_mode="X" request_status="GRANT" request_count="55" />
</Locks>
</Object>
_
これにより、メタデータ関連のブロッキングが発生する可能性があり(他のセッションがオブジェクトを作成している場合)、もちろん、作成中のセッションのみがターゲットテーブルにアクセスできます。
この部分は、質問に直接関連している可能性が高いです。
これらの違いに加えて、明示的なトランザクションを追加すると、_SELECT...INTO
_を使用するときに他のロック動作が変更される可能性があるという証拠がありました。シナリオも。
私が気付いた動作の要点は、明示的なトランザクションを使用した_SELECT...INTO
_により、クエリ内並列処理のデッドロックの発生率が非常に高くなることでした。これについての説明はありませんが、明示的なトランザクションが存在する場合、(ソースと宛先での)ロックが異なる可能性があります。そして、私が本当に提供できる唯一のアドバイスは、これらのデッドロックが問題になった場合に並列処理を完全に回避するためにOPTION (MAXDOP 1)
ヒントを追加することです。
私はこの奇妙なことについて話しました 私のブログで 、そしてそれについて バグを報告した もマイクロソフトに話しました。