Remusは、VARCHAR
列の最大長が推定行サイズに影響するため、SQL Serverが提供するメモリ許可を有効に指摘しています。
彼の答えの「これからのカスケード」の部分を拡大するために、もう少し研究を試みました。完全な説明や簡潔な説明はありませんが、ここで見つけました。
再現スクリプト
私のマシンでは、VARCHAR(256)
バージョンのインデックス作成に約10倍の時間がかかる偽のデータセットを生成する完全なスクリプトを作成しました。使用されるデータは全く同じであるが、最初の表は、実際の最大長さを使用して18
、75
、9
、15
、123
、および5
すべての列がの最大長さを使用しながら、256
第二のテーブルに。
元のテーブルのキーイング
ここでは、元のクエリが約20秒で完了し、論理読み取りがテーブルサイズ~1.5GB
(195Kページ、1ページあたり8K)に等しいことがわかります。
-- CPU time = 37674 ms, elapsed time = 19206 ms.
-- Table 'testVarchar'. Scan count 9, logical reads 194490, physical reads 0
CREATE CLUSTERED INDEX IX_testVarchar
ON dbo.testVarchar (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO
VARCHAR(256)テーブルのキーイング
このVARCHAR(256)
表では、経過時間が劇的に増加していることがわかります。
興味深いことに、CPU時間も論理読み取りも増加しません。これは、テーブルのデータがまったく同じであることを考えると理にかなっていますが、経過時間が非常に遅い理由を説明していません。
-- CPU time = 33212 ms, elapsed time = 263134 ms.
-- Table 'testVarchar256'. Scan count 9, logical reads 194491
CREATE CLUSTERED INDEX IX_testVarchar256
ON dbo.testVarchar256 (s1, s2, s3, s4)
WITH (MAXDOP = 8) -- Same as my global MAXDOP, but just being explicit
GO
I / Oおよび待機の統計:オリジナル
(私が書いた手順であるp_perfMonを使用して)もう少し詳細をキャプチャすると、I / Oの大部分がLOG
ファイルで実行されていることがわかります。実際のROWS
(メインデータファイル)のI / Oの量は比較的控えめであり、プライマリ待機タイプはでLATCH_EX
、メモリ内ページの競合を示しています。
我々はまた、私の回転ディスクが「悪い」と「ショッキングに悪い」の間のどこかにあることがわかりますポールランダルに従って :)
I / Oおよび待機の統計:VARCHAR(256)
VARCHAR(256)
バージョン、I / Oおよび待機統計は完全に違って見えます!ここでは、データファイル(I)のI / Oが大幅に増加してROWS
おり、ストールタイムによりPaul Randalが単に「WOW!」と言っています。
#1待機タイプが現在であることは驚くことではありませんIO_COMPLETION
。しかし、なぜそんなに多くのI / Oが生成されるのでしょうか?
実際のクエリプラン:VARCHAR(256)
クエリプランから、クエリSort
のVARCHAR(256)
バージョンで演算子が再帰的な流出(5レベルの深さ!)を持っていることがわかります。(元のバージョンでは流出はまったくありません。)
ライブクエリの進行状況:VARCHAR(256)
sys.dm_exec_query_profilesを使用して、SQL 2014+のライブクエリの進行状況を表示できます。元のバージョンでは、全体Table Scan
とはSort
、任意の流出(なしで処理されspill_page_count
たまま0
全体)。
VARCHAR(256)
ただし、このバージョンでは、ページスピルがSort
オペレーターにすぐに蓄積されることがわかります。以下は、クエリが完了する直前のクエリの進行状況のスナップショットです。ここのデータは、すべてのスレッドにわたって集計されます。
各スレッドを個別に掘り下げると、2つのスレッドが約5秒以内にソートを完了することがわかります(全体で20秒、テーブルスキャンに15秒かかった後)。すべてのスレッドがこの速度で進行した場合、VARCHAR(256)
インデックスの作成は元のテーブルとほぼ同じ時間で完了したことになります。
ただし、残りの6つのスレッドははるかに遅い速度で進行します。これは、メモリが割り当てられている方法と、スレッドがデータをこぼしているためにスレッドがI / Oによって保持されている方法が原因である可能性があります。確かにわかりません。
何ができる?
試すことを検討するかもしれない多くの事柄があります:
- ベンダーと協力して、以前のバージョンにロールバックします。それが不可能な場合は、将来のリリースで元に戻すことを検討できるように、この変更に満足していないことをベンダーに伝えてください。
- あなたのインデックスを追加する場合は、使用を検討して
OPTION (MAXDOP X)
いるX
現在のサーバーレベルの設定よりも低い数字です。OPTION (MAXDOP 2)
マシンでこの特定のデータセットを使用すると、VARCHAR(256)
バージョンは25 seconds
(8スレッドで3〜4分と比較して)で完了しました。こぼれる動作は、より高い並列性によって悪化する可能性があります。
- ハードウェアへの追加投資が考えられる場合は、システムのI / O(ボトルネックの可能性が高い)のプロファイルを作成し、SSDを使用して流出によるI / Oの遅延を減らすことを検討してください。
参考文献
Paul Whiteには、興味深いと思われるSQL Serverの種類の内部に関する素晴らしいブログ投稿があります。並列ソートのスピル、スレッドスキュー、およびメモリ割り当てについて少し説明します。