SQL Serverでは、クラスター化インデックスと非クラスター化インデックスの両方の行指向のストレージがBツリーとして編成されます。
(画像ソース)
クラスタ化インデックスと非クラスタ化インデックスの主な違いは、クラスタ化インデックスのリーフレベルがテーブルであるということです。これには2つの意味があります。
- クラスタ化インデックスリーフページの行には、テーブル内の(スパースではない)列ごとに何か(値または実際の値へのポインタ)が常に含まれています。
- クラスタ化インデックスは、テーブルのプライマリコピーです。
非クラスター化インデックスは、INCLUDE
句(SQL Server 2005以降)を使用してすべての非キー列を明示的に含めることでポイント1を実行することもできますが、これらは2次表現であり、データの別のコピー(テーブル自体)が常に存在します。
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つのクラスター化インデックスしか存在できません。
オンラインのSQLServerブックからの上記の引用は、多くの混乱を引き起こします
私の意見では、それはとしてはるかに良い言い回しになるでしょう。
クラスタ化インデックスのリーフレベルの行はテーブルの行であるため、テーブルごとに1つのクラスタ化インデックスしか存在できません。
この本のオンライン引用は正しくありませんが、非クラスター化インデックスとクラスター化インデックスの両方の「ソート」は物理的ではなく論理的であることを明確にする必要があります。リンクリストに従ってリーフレベルでページを読み取り、スロット配列の順序でページの行を読み取ると、インデックス行がソートされた順序で読み取られますが、物理的にはページがソートされない場合があります。クラスタ化インデックスでは、行は常にインデックスキーがfalseであるのと同じ順序でディスクに物理的に格納されるという一般的な考えがあります。
これはばかげた実装になります。たとえば、行が4GBのテーブルの中央に挿入された場合、SQL Serverは、新しく挿入された行用のスペースを確保するために、ファイル内の2GBのデータをコピーする必要はありません。
代わりに、ページ分割が発生します。クラスタ化インデックスと非クラスタ化インデックスの両方のリーフレベルの各ページにはFile: Page
、論理キー順に次のページと前のページのアドレス()があります。これらのページは、連続しているか、キー順になっている必要はありません。
たとえば、リンクされたページチェーンは1:2000 <-> 1:157 <-> 1:7053
ページ分割が発生すると、ファイルグループ内の任意の場所から新しいページが割り当てられます(混合エクステント、小さなテーブルの場合、またはそのオブジェクトに属する空でない均一エクステント、または新しく割り当てられた均一エクステントのいずれかから)。ファイルグループに複数のファイルが含まれている場合、これは同じファイルに含まれていない可能性があります。
論理的な順序と隣接性が理想的な物理バージョンとどの程度異なるかは、論理的な断片化の程度です。
1つのファイルで新しく作成されたデータベースで、次のように実行しました。
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)で構成されます。クラスター化インデックスの場合、行ロケーターは論理的です(クラスター化インデックスキー)。
後者の場合、非クラスター化インデックスにNCIキー列またはINCLUDE
-d列のいずれかとしてCIキー列がすでに自然に含まれている場合、何も追加されません。それ以外の場合、欠落しているCIキー列はサイレントにNCIに追加されます。
SQL Serverは、キー列が両方のタイプのインデックスに対して一意であることを常に確認します。ただし、一意として宣言されていないインデックスにこれが適用されるメカニズムは、2つのインデックスタイプ間で異なります。
クラスタ化インデックスはuniquifier
、既存の行と重複するキー値を持つすべての行に追加されます。これは単なる昇順の整数です。
一意として宣言されていない非クラスター化インデックスの場合、SQLServerは行ロケーターを非クラスター化インデックスキーにサイレントに追加します。これは、実際に重複している行だけでなく、すべての行に適用されます。
クラスター化された命名法とクラスター化されていない命名法は、列ストアのインデックスにも使用されます。ペーパー「SQLServer列ストアの機能強化」には次のように記載されています。
列ストアデータは実際にはどのキーでも「クラスター化」されていませんが、プライマリインデックスをクラスター化インデックスとして参照するという従来のSQLServerの規則を維持することにしました。