列サイズが大きくなった後、インデックスの作成に時間がかかるのはなぜですか?


18

ベンダーは、データベース全体のほとんどすべての列の列幅を変更しました。データベースは約7TB、9000以上のテーブルです。55億行のテーブルにインデックスを作成しようとしています。ベンダーのアップグレードの前に、2時間でインデックスを作成できました。今では数日かかります。彼らが行ったことは、varchar(xx)サイズをvarchar(256)に増やすことです。そのため、ほとんどの列はvarchar(18)またはvarchar(75)などでした。

とにかく、主キーは6列で構成され、幅は合計で126文字でした。アップグレード後、プライマリキーは1283文字になり、SQL Serverの900文字の制限に違反します。テーブルの列全体の幅は、1049の合計varcharカウントから4009の合計varcharカウントになりました。

データの増加はありません。テーブルはすべての列幅が増加する前よりも「スペース」を消費しませんが、インデックスのような単純なものを作成するためのパフォーマンスには不合理な時間がかかります。

列のサイズを大きくすることだけが行われたのに、作成とインデックス作成に非常に長い時間がかかる理由を誰でも説明できますか?

pkはクラスター化インデックスであるため、作成しようとしているインデックスは非クラスター化です。インデックスの作成を何度か試みた後、私たちはあきらめました。完了せずに4〜5日間実行されたと思います。

ファイルシステムのスナップショットを取得して非稼働環境でこれを試し、より静かなサーバーでデータベースを起動しました。

回答:


12

Remusは、VARCHAR列の最大長が推定行サイズに影響するため、SQL Serverが提供するメモリ許可を有効に指摘しています。

彼の答えの「これからのカスケード」の部分を拡大するために、もう少し研究を試みました。完全な説明や簡潔な説明はありませんが、ここで見つけました。

再現スクリプト

私のマシンでは、VARCHAR(256)バージョンのインデックス作成に約10倍の時間がかかる偽のデータセットを生成する完全なスクリプトを作成しました。使用されるデータは全く同じであるが、最初の表は、実際の最大長さを使用して1875915123、および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)

クエリプランから、クエリSortVARCHAR(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の種類の内部に関する素晴らしいブログ投稿があります。並列ソートのスピル、スレッドスキュー、およびメモリ割り当てについて少し説明します。


11

中間ソートテーブルは、2つのケース間で異なる方法で推定されます。これVARCHAR(256)により、「理想的な」要求と比較して、異なるメモリ許可要求(より大きくなります)および実際の許可がはるかに小さくなります(パーセント単位)。これがソート中の流出につながると思います。

Geoffからのスクリプトのテスト(10万行のみ)ソートの推定行サイズ(141Bと789B)の違いを明確に見ることができます。このことから、カスケードになります。


8
ポールは、おそらく開発チームが学習教材として使用するコールスタックを含む、より徹底的で完全な答えを証明すると確信しています。再び...
レムスRusanu
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.