2番目のINSERT
ステートメントが最初のステートメントより〜5倍遅いのはなぜですか?
生成されたログデータの量から、2番目のログは最小限のログ記録に適していないと思います。ただし、データ読み込みパフォーマンスガイドのドキュメントには、両方の挿入を最小限に記録できることが示されています。では、最小限のロギングが主要なパフォーマンスの違いである場合、2番目のクエリが最小限のロギングに適格ではないのはなぜですか?状況を改善するために何ができますか?
クエリ#1:INSERT ... WITH(TABLOCK)を使用して5MM行を挿入する
5MM行をヒープに挿入する次のクエリを考えてみます。このクエリは、によって報告されるトランザクションログデータで実行1 second
および生成64MB
されsys.dm_tran_database_transactions
ます。
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that correctly estimates that it will generate 5MM rows
FROM dbo.fiveMillionNumbers
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
クエリ#2:同じデータを挿入するが、SQLは行数を過小評価している
今度は非常によく似たクエリについて考えてみましょう。このクエリはまったく同じデータを操作しますがSELECT
、カーディナリティの推定値が低すぎるテーブル(または実際の本番のケースでは結合が多い複雑なステートメント)から偶然に描画されます。このクエリは、トランザクションログデータを実行し5.5 seconds
て生成し461MB
ます。
CREATE TABLE dbo.minimalLoggingTest (n INT NOT NULL)
GO
INSERT INTO dbo.minimalLoggingTest WITH (TABLOCK) (n)
SELECT n
-- Any table/view/sub-query that produces 5MM rows but SQL estimates just 1000 rows
FROM dbo.fiveMillionNumbersBadEstimate
-- Provides greater consistency on my laptop, where other processes are running
OPTION (MAXDOP 1)
GO
完全なスクリプト
テストデータを生成し、これらのシナリオのいずれかを実行するスクリプトの完全なセットについては、このPastebinを参照してください。SIMPLE
復旧モデルのデータベースを使用する必要があることに注意してください。
ビジネスコンテキスト
私たちは数百万行ものデータを半頻繁に移動していますが、実行時間とディスクI / O負荷の両方の観点から、これらの操作をできるだけ効率的にすることが重要です。最初はヒープテーブルを作成して使用するのINSERT...WITH (TABLOCK)
が良い方法だと思っていましたが、実際の本番シナリオで上記の状況を確認したため、自信がなくなってきました(ただし、クエリが複雑ではなく、簡略版はこちら)。
SELECT
の結果セットを生成する多数の結合を含む複雑なステートメントがありINSERT
ます。これらの結合は、最終テーブル挿入演算子(悪いUPDATE STATISTICS
呼び出しを介して再現スクリプトでシミュレートしました)のカーディナリティの見積もりを低くUPDATE STATISTICS
するため、問題を修正するコマンドを発行するほど簡単ではありません。Cardinality Estimatorが理解しやすいようにクエリを簡略化することは良いアプローチかもしれませんが、特定の複雑なビジネスロジックを実装することは簡単ではありません。