適用対象:SQL Server
Azure SQL Managed Instance
SQL Server データベース エンジンのインスタンスからの I/O には、論理読み取りと物理読み取りがあります。 論理読み取りは、データベース エンジンが バッファー キャッシュ (バッファープールとも呼ばれます) からページを要求するたびに発生します。 ページが現在バッファー キャッシュに存在しない場合、最初に物理読み取りによってページがディスクからキャッシュにコピーされます。
データベース エンジンのインスタンスによって生成される読み取り要求は、リレーショナル エンジンによって制御され、ストレージ エンジンによって最適化されます。 リレーショナル エンジンは、最も効果的なアクセス方法 (テーブル スキャン、インデックス スキャン、キー付き読み取りなど) を決定します。 ストレージ エンジンのアクセス方法とバッファー マネージャー コンポーネントは、実行する読み取りの一般的なパターンを決定し、アクセス方法を実装するために必要な読み取りを最適化します。 バッチを実行するスレッドが、読み取りのスケジュールを設定します。
先読み
データベース エンジンでは、先行読み取りと呼ばれるパフォーマンス最適化メカニズムがサポートされています。 先読みでは、クエリ実行プランを満たすために必要なデータ ページとインデックス ページを予測し、クエリで使用される前にページをバッファー キャッシュに取り込みます。 このプロセスにより、CPU とディスクの両方を最大限に活用して、計算と I/O が重複できるようになります。
先読みメカニズムを使用すると、データベース エンジンは 1 つのファイルから最大 64 ページ (512 KB) の連続したページを読み取ることができます。 読み取りは、バッファー キャッシュ内の適切な数の(おそらく連続していない)バッファーに対して、単一のスキャッタ・ギャザー読み取りとして行われます。 範囲内のページのいずれかがバッファー キャッシュに既に存在する場合、読み取りの完了時に、読み取りからの対応するページは破棄されます。 対応するページが既にキャッシュに存在する場合は、ページの範囲がどちらかの端から "トリミング" される場合もあります。
先行読み取りには、 データ ページ 用と インデックス ページ用の 2 種類があります。
データ ページの読み取り
データベース エンジンがデータ ページの読み取りに使用するテーブル スキャンは効率的です。 SQL Server データベース内の IAM (Index Allocation Map) ページには、テーブルまたはインデックスで使用されているエクステントの一覧が格納されています。 ストレージ エンジンは IAM を読み取ることで、読み取る必要のあるディスク アドレスが並べ替えられたリストを構築できます。 これによりストレージ エンジンは、ディスク上の位置に基づき、順番に行われる大量の順次読み取りとして、I/O を最適化できます。 IAM ページの詳細については、「 オブジェクトで使用される領域の管理」を参照してください。
インデックス ページの読み取り
ストレージ エンジンは、インデックス ページをキー順で直列に読み取ります。 次の図は、キーのセットを格納しているリーフ ページのセットと、リーフ ページをマップしている中間インデックス ノードの例を示しています。 インデックス内のページの構造の詳細については、「 クラスター化インデックスと非クラスター化インデックス」を参照してください。
ストレージ エンジンは、リーフ レベルの上にある中間インデックス ページ内の情報を使用して、キーを格納しているページに対して、直列先行読み取りのスケジュールを設定します。 ABC
からDEF
までのすべてのキーに対して要求が行われた場合、ストレージ エンジンは最初にリーフ ページの上にあるインデックス ページを読み取ります。 ただし、各データ ページを 504 ページから 556 ページ (指定した範囲のキーを持つ最後のページ) に順番に読み取るだけではありません。 ストレージ エンジンは、中間インデックス ページをスキャンし、読み取りが必要なリーフ ページのリストを構築します。 ストレージエンジンはすべての読み取りをキー順にスケジュールします。 ストレージ エンジンは、ページ 504/505 と 527/528 が連続していることも認識し、単一のスキャッター読み取りを実行して、隣接するページを 1 回の操作で取得します。 直列操作で多くのページを取得する場合、ストレージ エンジンは一度に行う読み取りブロックのスケジュールを設定します。 これらの読み取りのサブセットが完了すると、ストレージ エンジンは、必要なすべての読み取りがスケジュールされるまで、同じ数の新しい読み取りをスケジュールします。
ストレージ エンジンは プリフェッチを 使用して、非クラスター化インデックスからのベース テーブル参照を高速化します。 非クラスター化インデックスのリーフ行は、それぞれの特定のキー値を格納しているデータ行へのポインターを格納しています。 ストレージ エンジンは、非クラスター化インデックスのリーフ ページを読み取ると、ポインターが既に取得されているデータ行の非同期読み取りのスケジュールも開始します。 これにより、ストレージ エンジンは、非クラスター化インデックスのスキャンを完了する前に、基になるテーブルからデータ行を取得できます。 テーブルがクラスター化インデックスを保持しているかどうかに関係なく、プレフェッチが使用されます。 SQL Server Enterprise Edition では、SQL Server の他のエディションよりもプリフェッチが多く使用されるため、より多くのページを先読みできます。 プリフェッチのレベルはどのエディションでも構成できません。 非クラスター化インデックスの詳細については、「 クラスター化インデックスと非クラスター化インデックス」を参照してください。
拡張スキャン
SQL Server Enterprise エディションでは、高度なスキャン機能を使用すると、複数のタスクで完全なテーブル スキャンを共有できます。 Transact-SQL ステートメントの実行プランがテーブルのデータ ページのスキャンを必要とし、そのテーブルが既に他の実行プラン用にスキャンされていることをデータベース エンジンが検出した場合、データベース エンジンは、2 番目のスキャンの現在位置で 2 番目のスキャンを 1 番目のスキャンに結合します。 データベース エンジンは各ページを 1 回だけ読み取り、各ページの行を両方の実行プランに渡します。 これが、テーブルの末尾に達するまで続けられます。
その時点で、最初の実行プランにはスキャンの完全な結果が含まれます。 ただし、2 番目の実行プランでは、進行中のスキャンに参加する前に、読み取られたデータ ページを引き続き取得する必要があります。 2 番目の実行プランのスキャンは、折り返してテーブルの最初のデータ ページに戻り、1 番目のスキャンに結合した位置に達するまでスキャンを実行します。 このようにして、スキャンはいくつでも組み合わせることができます。 データベース エンジンは、すべてのスキャンが完了するまでデータ ページをループし続けます。 このメカニズムは "merry-go-round scanning" とも呼ばれ、 SELECT
ステートメントから返される結果の順序を ORDER BY
句なしで保証できない理由を示します。
たとえば、500,000 ページを持つテーブルがあるとします。 UserA
は、テーブルのスキャンを必要とする Transact-SQL ステートメントを実行します。 そのスキャンで 100,000 ページが処理されると、 UserB
は同じテーブルをスキャンする別の Transact-SQL ステートメントを実行します。 データベース エンジンは、100,001 ページ以降のページに対する 1 セットの読み取り要求をスケジュールに組み込み、各ページの行を両方のスキャンに渡します。 スキャンが 200,000 ページに達すると、 UserC
は同じテーブルをスキャンする別の Transact-SQL ステートメントを実行します。 データベース エンジンは、200,001 ページから読み取った各ページの行を 3 つのスキャンすべてに渡します。 500,000 行目を読み取ると、 UserA
のスキャンが完了し、 UserB
と UserC
スキャンが折り返され、ページ 1 以降のページの読み取りが開始されます。 データベース エンジンが 100,000 ページになると、 UserB
のスキャンが完了します。 その後、 UserC
のスキャンは、200,000 ページを読み取るまで単独で実行されます。 このページ時点ですべてのスキャンが完了したことになります。
拡張スキャンを使用しなければ、ユーザーが互いにバッファー領域の確保を求めて競合することになり、ディスク アームの競合が発生します。 また、一度読み取られたページを複数のユーザーが共有するのではなく、同じページがユーザーごとにその都度読み取られるため、パフォーマンスが低下し、リソースに負荷がかかります。