ラッチに関する一連の記事を続けて、今回はDBCC_OBJECT_METADATAラッチについて説明し、特定の状況下でSQLServer2016より前の整合性チェックの主要なボトルネックになる可能性があることを示します。この問題は、DBCC CHECKDB、DBCC CHECKTABLE、およびDBCC CHECKFILEGROUPに影響しますが、わかりやすくするために、この投稿の残りの部分ではDBCCCHECKDBを参照します。
古いバージョンに影響する問題について書いているのはなぜかと思うかもしれませんが、SQL Server 2014や古いインスタンスはまだたくさんあるので、私のシリーズでは有効なトピックです。
このシリーズの前にシリーズの最初の投稿を読むことを強くお勧めします。そうすれば、ラッチに関する一般的な背景知識をすべて身に付けることができます。
DBCC_OBJECT_METADATAラッチとは何ですか?
このラッチを説明するために、DBCCCHECKDBがどのように機能するかについて少し説明する必要があります。
DBCC CHECKDBが実行する膨大な数の整合性チェックの中には、非クラスター化インデックスの正確性のチェックがあります。具体的には、DBCCCHECKDBは次のことを確認します。
- すべての非クラスター化インデックスのすべての非クラスター化インデックスレコードに対して、ベーステーブル(ヒープまたはクラスター化インデックスのいずれか)に正確に1つの「一致する」データレコードがあります。
- テーブル内のすべてのデータレコードについて、フィルタリングされたインデックスを考慮して、テーブルに対して定義された各非クラスター化インデックスに正確に1つの「一致する」非クラスター化インデックスレコードがあります
これがどのように行われるかについて詳しく説明することなく、DBCC CHECKDBは、テーブル内のデータレコードごとに、非クラスター化インデックスごとに存在する必要のある非クラスター化インデックスレコードを作成し、作成された非クラスター化インデックスレコードが実際の非クラスター化インデックスレコード。非クラスター化インデックスに計算列が含まれている場合(非クラスター化インデックスキーの一部として、またはINCLUDEd列として)、DBCC CHECKDBは、インデックスレコードを作成するときに使用する計算列の値を把握する必要があります。
非クラスター化インデックスの正確性チェックと同様に、永続化がある場合 テーブルの定義で計算された列、次にテーブルの各データレコードについて、DBCC CHECKDBは、その列が非クラスター化インデックスの一部であるかどうかに関係なく、永続化された値が正しいことを確認する必要があります。
では、計算された列の値をどのように計算するのでしょうか?
クエリプロセッサは、「式エバリュエーター」と呼ばれる、計算された列の値を計算するためのメカニズムを提供します。 DBCC CHECKDBはその関数を呼び出し、適切なメタデータ情報とデータレコードを提供します。式エバリュエーターは、メタデータに格納されている計算列の定義とデータレコードの値を使用して、DBCCCHECKDBが使用する計算列の値を返します。 。式エバリュエーターの内部動作はDBCCコードの制御外ですが、式エバリュエーターを使用できるようにするには、最初にラッチを取得する必要があります。 DBCC_OBJECT_METADATAラッチ。
ラッチはどのようにしてボトルネックになりますか?
問題は次のとおりです。式エバリュエーターを使用する前にDBCC_OBJECT_METADATAラッチを取得できる許容可能なモードは1つだけであり、それがEXモード(排他的)です。また、シリーズの紹介記事を読むとわかるように、一度に1つのスレッドだけがEXモードでラッチを保持できます。
このすべての情報をまとめます。データベースに計算列が永続化されている場合、または計算列が含まれている非クラスター化インデックスの場合は、式エバリュエーターを使用する必要があります。 SQL ServerエディションがEnterpriseの場合、DBCC CHECKDBは並列処理を使用できるため、さまざまなチェックを実行する複数のスレッドがあります。また、EXモードでラッチを取得しようとする複数のスレッドがあるとすぐに、そのラッチがボトルネックになります。ボトルネックがどれだけ大きくなるかは、式エバリュエーターを使用する必要がある量によって異なります。したがって、より永続的な計算列または計算列を使用する非クラスター化インデックスがあり、それらのテーブルのテーブル行の数が多いほど、 DBCC_OBJECT_METADATAラッチのボトルネックが大きくなります。
ただし、このボトルネックはSQL Server2016より前のバージョンのSQLServerでのみ発生することを忘れないでください。SQLServer2016では、Microsoftは、デフォルトで計算列を使用して非クラスター化インデックスのチェックをオフにし、WITHの場合にのみ実行することで、ボトルネックを「修正」することを決定しました。 EXTENDED_LOGICAL_CHECKSオプションが使用されます。
ボトルネックを示す
計算列を永続化したデータベースまたは計算列を含む非クラスター化インデックスのいずれかを含むデータベースでDBCCCHECKDBを実行すると、ボトルネックを簡単に再現できます。Microsoftが提供するAdventureWorksデータベースはその好例です。 SQLServerのバージョンのAdventureWorksのバックアップをここからダウンロードできます。 SQL Server 2014インスタンス(32コアのDell R720)でAdventureWorks2014データベースを使用していくつかのテストを実行し、Jonathanのスクリプトを使用してデータベースを数百GBに拡大しました。
サーバーMAXDOPを0に設定してDBCCCHECKDBを実行すると、実行に5時間以上かかりました。 LATCH_EX待機タイプは待機の約30%を占め、各待機は1ミリ秒のわずかな時間であり、LATCH_EX待機の99%はDBCC_OBJECT_METADATAラッチに対するものでした。
次のコードを使用して、計算列を含む非クラスター化インデックスを探しました。
SELECT [s].[name] AS [Schema], [o].[name] AS [Object], [i].[name] AS [Index], [c].[name] AS [Column], [ic].* FROM sys.columns [c] JOIN sys.index_columns [ic] ON [ic].[object_id] = [c].[object_id] AND [ic].[column_id] = [c].[column_id] JOIN sys.indexes [i] ON [i].[object_id] = [ic].[object_id] AND [i].[index_id] = [ic].[index_id] JOIN sys.objects [o] ON [i].[object_id] = [o].[object_id] JOIN sys.schemas [s] ON [o].[schema_id] = [s].[schema_id] WHERE [c].[is_computed] = 1;
そのコードは、AdventureWorks2014データベースで6つの非クラスター化インデックスを検出しました。 6つのインデックスすべてを無効にし(ALTER INDEX…DISABLEを使用)、DBCC CHECKDBを再実行すると、約18分で完了しました。したがって、DBCC_OBJECT_METADATAラッチのボトルネックは、DBCCCHECKDBの実行速度が16倍以上遅くなる主な要因でした。
概要
残念ながら、計算列を使用して非クラスター化インデックスを無効にする(その後、ALTER INDEX…REBUILDを使用して再度有効にする)ことは、SQL Server 2016より前のバージョンのDBCC_OBJECT_METADATAラッチのボトルネックを取り除き、DBCCの他のすべての機能を維持する*唯一の*方法です。 CHECKDB。非クラスター化インデックスを無効にすることは、アクティビティのメンテナンスウィンドウがゼロでない限り、実稼働環境で実行したいことではない可能性があります。つまり、backup-copy-restore-CHECKDBメソッドを使用して整合性チェックが別のサーバーにオフロードされた場合にのみ、これらの非クラスター化インデックスを無効にしてボトルネックを取り除くことになります。
もう1つの方法は、DBCCCHECKDBを実行するときにWITHPHYSICAL_ONLYオプションを使用することですが、すべての詳細な論理チェックを見逃してしまうため、解決策としてそれを推奨することはあまり好きではありません。