SQL Serverの行指向ストレージでは、クラスター化インデックスと非クラスター化インデックスの両方がBツリーとして編成されます。
(画像ソース)
クラスタ化インデックスと非クラスタ化インデックスとの間の主な違いは、クラスタ化インデックスのリーフレベルがことであるであるテーブル。これには2つの意味があります。
- クラスター化インデックスリーフページの行には、テーブル内の(スパースでない)列ごとに何かが常に含まれています(値または実際の値へのポインター)。
- クラスタ化インデックスは、テーブルのプライマリコピーです。
非クラスター化インデックスは、INCLUDE
句(SQL Server 2005以降)を使用してすべての非キー列を明示的に含めることによってポイント1を実行することもできますが、それらはセカンダリ表現であり、常にデータの別のコピー(テーブル自体)があります。
CREATE TABLE T
(
A INT,
B INT,
C INT,
D INT
)
CREATE UNIQUE CLUSTERED INDEX ci ON T(A,B)
CREATE UNIQUE NONCLUSTERED INDEX nci ON T(A,B) INCLUDE (C,D)
上記の2つのインデックスはほぼ同じになります。上位レベルのインデックスページにはキー列の値が含まれA,B
、リーフレベルページにはA,B,C,D
データ行自体は1つの順序でしか並べ替えることができないため、テーブルごとにクラスター化インデックスは1つしか存在できません。
上記のSQL Server Booksオンライン引用は、多くの混乱を引き起こしています。
私の意見では、それは次のように表現される方がはるかに良いでしょう。
クラスタ化インデックスのリーフレベルの行があるため、テーブルごとに1つだけクラスタ化インデックスが存在することができますテーブル行。
書籍のオンライン見積もりは不正確ではありませんが、非クラスター化インデックスとクラスター化インデックスの両方の「ソート」は物理的ではなく論理的であることを明確にする必要があります。リンクされたリストに従ってリーフレベルでページを読み取り、スロット配列順にページの行を読み取る場合、インデックス行をソート順に読み取りますが、物理的にページはソートされない場合があります。クラスター化されたインデックスでは、行は常にインデックスキーがfalse であるのと同じ順序でディスクに物理的に格納されるという一般的な見方です。
これはばかげた実装になります。たとえば、4GBのテーブルの途中に行が挿入された場合、SQL Serverは、ファイルに2GBのデータをコピーして、新しく挿入された行のためのスペースを空ける必要はありません。
代わりに、ページ分割が発生します。クラスター化インデックスと非クラスター化インデックスの両方のリーフレベルにある各ページにはFile:Page
、論理キーの順序で次のページと前のページのアドレス()があります。これらのページは連続している必要も、キー順である必要もありません。
たとえば、リンクされたページチェーンは 1:2000 <-> 1:157 <-> 1:7053
ページ分割が発生すると、新しいページがファイルグループ内の任意の場所から割り当てられます(小さいテーブルの混合エクステント、またはそのオブジェクトに属する空でない均一エクステント、または新しく割り当てられた均一エクステント)。ファイルグループに複数のファイルが含まれている場合、これは同じファイル内にない場合もあります。
論理的な順序と連続性が理想的な物理バージョンと異なる程度は、論理的な断片化の程度です。
新しく作成された単一ファイルのデータベースで、私は以下を実行しました。
CREATE TABLE T
(
X TINYINT NOT NULL,
Y CHAR(3000) NULL
);
CREATE CLUSTERED INDEX ix
ON T(X);
GO
--Insert 100 rows with values 1 - 100 in random order
DECLARE @C1 AS CURSOR,
@X AS INT
SET @C1 = CURSOR FAST_FORWARD
FOR SELECT number
FROM master..spt_values
WHERE type = 'P'
AND number BETWEEN 1 AND 100
ORDER BY CRYPT_GEN_RANDOM(4)
OPEN @C1;
FETCH NEXT FROM @C1 INTO @X;
WHILE @@FETCH_STATUS = 0
BEGIN
INSERT INTO T (X)
VALUES (@X);
FETCH NEXT FROM @C1 INTO @X;
END
次に、ページレイアウトを確認しました
SELECT page_id,
X,
geometry::Point(page_id, X, 0).STBuffer(1)
FROM T
CROSS APPLY sys.fn_PhysLocCracker( %% physloc %% )
ORDER BY page_id
結果はいたるところにありました。キー順の最初の行(値1-下の矢印で強調表示)は、ほぼ最後の物理ページにありました。
断片化は、インデックスを再構築または再編成して、論理的な順序と物理的な順序の相関を高めることにより、削減または削除できます。
実行した後
ALTER INDEX ix ON T REBUILD;
私は以下を得ました
テーブルにクラスタ化インデックスがない場合、それはヒープと呼ばれます。
非クラスター化インデックスは、ヒープまたはクラスター化インデックスのいずれかに構築できます。これらには常に、ベーステーブルに戻る行ロケータが含まれています。ヒープの場合、これは物理的な行識別子(rid)であり、3つのコンポーネント(File:Page:Slot)で構成されます。クラスタ化インデックスの場合、行ロケータは論理的です(クラスタ化インデックスキー)。
後者の場合、非クラスター化インデックスにすでにCIキー列がNCIキー列またはINCLUDE
-d列として含まれている場合、何も追加されません。それ以外の場合、欠落しているCIキー列はサイレントにNCIに追加されます。
SQL Serverは常に、キー列が両方のタイプのインデックスに対して一意であることを保証します。ただし、これが一意として宣言されていないインデックスに適用されるメカニズムは、2つのインデックスタイプで異なります。
クラスタ化インデックスはuniquifier
、既存の行を複製するキー値を持つすべての行に追加されます。これは単に昇順の整数です。
一意として宣言されていない非クラスター化インデックスの場合、SQL Serverは行ロケーターを非クラスター化インデックスキーに警告なしに追加します。これは、実際に重複している行だけでなく、すべての行に適用されます。
クラスタ化された用語と非クラスタ化された用語は、列ストアインデックスにも使用されます。紙SQL Serverの列ストアの拡張状態
列ストアデータは実際にはどのキーでも「クラスター化」されていませんが、プライマリインデックスをクラスター化インデックスとして参照するという従来のSQL Serverの規則を維持することにしました。