多くのSQLServerワークロード、特にOLTPでは、データベースのトランザクションログがボトルネックになる可能性があり、トランザクションの完了にかかる時間が長くなります。ほとんどの人は、I / Oサブシステムが本当のボトルネックであり、ワークロードによって生成されるトランザクションログの量に追いつくことができないと考えています。
トランザクションログの書き込みレイテンシ
トランザクションログへの書き込み操作の待機時間は、sys.dm_io_virtual_file_stats
を使用して監視できます。 DMVおよびWRITELOG
との相関 システムで発生している待機。 2011年にトランザクションログI/Oを分析するデモビデオを録画したので、この投稿ではそのすべてを繰り返すことはしません。ここでビデオとここでデモコードを入手できます(すぐに本番環境で実行するのに適しています)。
書き込みレイテンシがI/Oサブシステムに期待するよりも高い場合、一般的な想定と同様に、I/Oサブシステムは追いつくことができません。それは、I / Oサブシステムを改善する必要があるということですか?必ずしもそうとは限りません。
多くのクライアントシステムでは、生成されるログレコードのかなりの部分が不要であり、生成されるログレコードの数を減らすことができれば、ディスクに書き込まれるトランザクションログの量を減らすことができます。これにより、書き込みレイテンシが短縮され、トランザクションの完了時間が短縮されます。
無関係なログレコードが生成される主な原因は2つあります。未使用の非クラスター化インデックスと、インデックスが断片化することです。
未使用の非クラスター化インデックス
レコードをテーブルに挿入するときは常に、テーブルで定義されている各非クラスター化インデックスにレコードを挿入する必要があります(ただし、適切なフィルターを使用してフィルター処理されたインデックスは、この時点から無視します)。これは、テーブルの挿入ごとに、非クラスター化インデックスごとに少なくとも1つの追加のログレコードが生成されることを意味します。同じことがテーブル内のレコードの削除にも当てはまります。一致するレコードをすべての非クラスター化インデックスから削除する必要があります。テーブルレコードの更新の場合、非クラスター化インデックスレコードは、非クラスター化インデックスキー列または含まれる列が更新の一部である場合にのみ更新されます。
もちろん、これらの操作は、テーブルに関して各非クラスター化インデックスを正しく保つために必要ですが、非クラスター化インデックスがワークロードによって使用されていない場合、操作とそれらによって生成されるログレコードは不要なオーバーヘッドです。さらに、これらの未使用のインデックスが断片化された場合(これについてはこの投稿の後半で説明します)、通常のインデックスメンテナンスタスクもそれらに対して機能し、さらに多くのログレコードを生成します(インデックスREBUILD
から) またはREORGANIZE
操作)完全に不必要に。
未使用のインデックスは、テーブル列ごとに誤ってインデックスを作成したり、欠落しているインデックスDMVによって提案されたすべてのインデックスを作成したり、DatabaseTuningAdvisorによって提案されたすべてのインデックスを作成したりするなどのさまざまなソースから取得されます。また、ワークロードの特性が変更されたため、以前は有用なインデックスであったものが使用されなくなった可能性もあります。
どこから来たとしても、オーバーヘッドを減らすために未使用のインデックスを削除する必要があります。 sys.dm_db_index_usage_stats DMVを使用して、どのインデックスが使用されていないかを判断できます。DMVの正しい使用方法を説明している、同僚のKimberly L. Tripp(ここ)とJoe Sack(こことここ)の投稿を読むことをお勧めします。
インデックスの断片化
ほとんどの人は、インデックスの断片化を、大量のデータを読み取らなければならないクエリに影響を与える問題と考えています。これは断片化が引き起こす可能性のある問題の1つですが、断片化はそれがどのように発生するかによっても問題になります。
断片化は、ページ分割と呼ばれる操作によって発生します。ページ分割の最も単純な原因は、インデックスレコードを特定のページに挿入する必要があり(そのキー値のため)、ページに十分な空き領域がない場合です。このシナリオでは、次の操作が行われます。
- 新しいインデックスページが割り当てられ、フォーマットされます
- ページ全体の一部のレコードが新しいページに移動されるため、必要なページに空きスペースが作成されます
- 新しいページはインデックス構造にリンクされています
- 新しいレコードが必要なページに挿入されます
これらの操作はすべてログレコードを生成します。ご想像のとおり、これは、ページ分割を必要としないページに新しいレコードを挿入するために必要な量よりも大幅に多くなる可能性があります。 2009年に、トランザクションログの観点からページ分割コストの分析をブログに書きましたが、ページ分割によって通常の挿入の40倍以上のトランザクションログが生成される場合がありました。
追加コストを削減するための最初のステップは、前述のように未使用のインデックスを削除して、ページ分割が発生しないようにすることです。 2番目のステップは、sys.dm_db_index_physical_stats
を使用して、断片化されている(したがって、ページ分割が発生している必要がある)残りのインデックスを特定することです。 DMV(または新しいSQL Sentry Fragmentation Manager)と、インデックスフィルファクターを使用してそれらの中に空き領域を積極的に作成します。フィルファクターは、SQL Serverに、インデックスの構築、再構築、または再編成時にインデックスページに空のスペースを残すように指示します。これにより、ページ分割を必要とせずに新しいレコードを挿入できるスペースが確保され、生成される余分なログレコードが削減されます。
もちろん、無料で提供されるものはありません。フィルファクターを使用する場合のトレードオフは、インデックスに余分なスペースを積極的にプロビジョニングして、ログレコードが生成されないようにすることです。ただし、これは通常、適切なトレードオフです。フィルファクターの選択は比較的簡単で、ここでブログに書いています。
概要
トランザクションログファイルの書き込みレイテンシを短縮することは、必ずしもより高速なI / Oサブシステムに移行したり、ファイルをI/Oサブシステムの独自の部分に分離したりすることを意味するわけではありません。データベース内のインデックスを簡単に分析することで、生成されるトランザクションログレコードの量を大幅に削減でき、それに応じて書き込みレイテンシを削減できる可能性があります。
トランザクションログのパフォーマンスに影響を与える可能性のある、他のより微妙な問題があります。これらについては、今後の投稿で詳しく説明します。