クラスター化インデックスと非クラスター化インデックスのパフォーマンスの違い


22

私が読んでいたClusteredNon Clustered Indexes

Clustered Index-データページが含まれています。つまり、完全な行情報がクラスター化インデックス列に表示されます。

Non Clustered Index-クラスター化インデックス列(利用可能な場合)またはファイル識別子+ページ番号+ページ内の合計行の形式の行ロケーター情報のみが含まれます。これは、クエリエンジンが実際のデータを見つけるために追加の手順を実行する必要があることを意味します。

クエリ -私たちは、テーブルが一つだけ持つことができることを知っているとどのように私は実用的な例の助けを借りて、パフォーマンスの違いを確認することができますClustered Indexし、提供sortingClustered Index ColumnNon Clustered Index提供していないsortingと999をサポートすることができますNon Clustered IndexesSQL Server 2008にして249 SQL Server 2005


2
何をするときのパフォーマンスの違い、そのテーブルでどのような仕事をしたいのか、すべてのニーズに合う単一のソリューションはありません
-Lamak

2
おそらくここでいくつかの具体的な議論。stackoverflow.com/questions/91688/...は stackoverflow.com/questions/5070529/... stackoverflow.com/questions/1251636/...私たちは、クラスタ化と非クラスタ化インデックスの違いについて論文を書くことができますが、私たちは考えていませんあなたが読むことができる、まだそこにないものは何でも言うでしょう。
アーロンバートランド

4
「これは、クエリエンジンが実際のデータを見つけるために追加の手順を実行する必要があることを意味します。」実際、必要なのがインデックスカバーされている列だけである場合、非クラスター化インデックスでターゲット行を見つけた後、追加の手順を実行する必要ありません。SQL Serverは、非クラスター化インデックスでカバーされていない列が必要な場合にのみ、ブックマークルックアップを実行する必要があります
ニックチャマス

回答:


43

非常に重要な概念であるため、非常に良い質問です。ただし、これは大きなトピックであり、基本概念を理解できるように簡略化して説明します。

まず、クラスター化インデックス思考テーブルが表示されます。SQLサーバーでは、テーブルにクラスター化インデックスが含まれていない場合、それはヒープです。テーブルにクラスター化インデックスを作成すると、実際にはテーブルがBツリータイプの構造に変換されます。クラスター化インデックスはテーブルであり、テーブルとは別ではありません

クラスタ化インデックスを1つしか持てないのはなぜだろうと思ったことはありませんか?クラスター化インデックスが2つある場合、テーブルのコピーが2つ必要になります。結局データが含まれています。

これを簡単な例を使用して説明します。

注:この例でテーブルを作成し、300万件以上のランダムエントリを入力しました。次に、実際のクエリを実行し、実行計画をここに貼り付けました。

本当に把握する必要があるのは、表記法または運用効率です。次の表があると仮定しましょう。

CREATE TABLE [dbo].[Customer](
[CustomerID] [int] IDENTITY(1,1) NOT NULL,
[CustomerName] [varchar](100) NOT NULL,
[CustomerSurname] [varchar](100) NOT NULL,
CONSTRAINT [PK_Customer] PRIMARY KEY CLUSTERED 
(
[CustomerID] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , IGNORE_DUP_KEY = OFF,ALLOW_ROW_LOCKS  = ON
  , ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

したがって、ここには、CustomerIDにクラスター化されたキーを持つ基本テーブルがあります(プライマリーキーはデフォルトでクラスター化されています)。したがって、テーブルは主キーCustomerIDに基づいて配置/順序付けされます。中間レベルには、CustomerID値が含まれます。データページには行全体が含まれるため、テーブル行になります。

また、CustomerNameフィールドに非クラスター化インデックスを作成します。次のコードで実行します。

CREATE NONCLUSTERED INDEX [ix_Customer_CustomerName] ON [dbo].[Customer] 
 (
[CustomerName] ASC
 )WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF
  , SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF
  , DROP_EXISTING = OFF, ONLINE = OFF
  , ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]

そのため、このインデックスでは、データページ/リーフレベルのノードに、クラスター化インデックスの中間レベルへのポインターがあります。インデックスは、CustomerNameフィールドの周りに配置/順序付けされます。したがって、中間レベルにはCustomerName値が含まれ、リーフレベルにはポインターが含まれます(これらのポインター値は、実際には主キー値またはCustomerID列です)。

そうです、次のクエリを実行すると:

SELECT * FROM Customer WHERE CustomerID = 1 

SQLはおそらく、シーク操作を介してクラスター化インデックスを読み取ります。シーク操作は、順次検索であるスキャンよりもはるかに効率的なバイナリ検索です。したがって、上記の例では、インデックスが読み取られ、バイナリ検索を使用することで、SQLは探している基準に一致しないデータを削除できます。クエリプランについては、添付のスクリーンショットを参照してください。

ここに画像の説明を入力してください

したがって、シーク操作の操作数またはO表記は次のとおりです。

  1. 検索対象の値を中間レベルの値と比較して、クラスター化インデックスでバイナリ検索を実行します。
  2. 一致する値を返す

したがって、2つの操作です。ただし、次のクエリを実行した場合:

SELECT * FROM Customer WHERE CustomerName ='John'

SQLは、CustomerNameの非クラスター化インデックスを使用して検索を実行します。ただし、これは非クラスター化インデックスであるため、行のすべてのデータが含まれているわけではありません。

そのため、SQLは中間レベルで検索を行って一致するレコードを見つけ、返された値を使用して検索を実行し、クラスター化インデックス(別名テーブル)で別の検索を行って実際のデータを取得します。これはわかりにくいですが、読み進めていくと明らかになります。

非クラスター化インデックスには、CustomerNameフィールド(中間ノードに格納されているインデックスフィールド値)とCustomerIDであるデータへのポインターのみが含まれているため、インデックスにはCustomerSurnameのレコードがありません。CustomerSurnameは、クラスター化インデックスまたはテーブルから取得する必要があります。

このクエリを実行すると、次の実行計画が得られます。

ここに画像の説明を入力してください

上記のスクリーンショットで気付くべき2つの重要なことがあります

  1. SQLには、インデックスがありません(緑色のテキスト)と言われています。SQLは、CustomerIDとCustomerSurnameを含むCustomerNameにインデックスを作成することを提案しています。
  2. また、クエリの時間の99%が主キーインデックス/クラスター化インデックスのキールックアップに費やされていることがわかります。

SQLがCustomerNameのインデックスを再度提案するのはなぜですか?インデックスにはCustomerIDのみが含まれており、CustomerName SQLはテーブル/クラスター化インデックスからCustomerSurnameを見つける必要があるためです。

インデックスを作成し、CustomerSurname列をインデックスに含めた場合、SQLは非クラスター化インデックスを読み取るだけでクエリ全体を満たすことができます。これが、SQLが非クラスター化インデックスの変更を提案している理由です。

ここでは、クラスター化されたキーからCustomerSurname列を取得するためにSQLが必要とする追加の操作を確認できます。

したがって、操作の数は次のとおりです。

  1. 検索対象の値を中間レベルの値と比較して、非クラスター化インデックスでバイナリ検索を実行します
  2. 一致するノードの場合、クラスター化インデックス内のデータへのポインターを含むリーフレベルノードを読み取ります(リーフレベルノードには、主キー値が含まれます)。
  3. 返される値ごとに、クラスター化インデックス(テーブル)で読み取りを実行し、ここで行の値を取得します。CustomerSurnameを読み取ります。
  4. 一致する行を返す

値を取得するための4つの操作です。クラスター化インデックスの読み取りと比較して、必要な操作の2倍。すべてのデータが含まれているため、クラスター化インデックスが最も強力なインデックスであることを示しています。

最後のポイントを明確にするために。非クラスター化インデックスのポインターが主キー値であると言うのはなぜですか?非クラスター化インデックスのリーフレベルノードにプライマリキー値が含まれていることを示すために、クエリを次のように変更します。

SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'

このクエリでは、SQLは非クラスター化インデックスからCustomerIDを読み取ることができます。クラスタ化インデックスを検索する必要はありません。これは、次のような実行計画で確認できます。

ここに画像の説明を入力してください

このクエリと前のクエリの違いに注意してください。ルックアップはありません。SQLは、非クラスター化インデックス内のすべてのデータを検索できます。

クラスター化インデックスはテーブルであり、非クラスター化インデックスにはすべてのデータが含まれていないことを理解できることを願っています。バイナリ検索は実行できますが、クラスター化インデックスのみにすべてのデータが含まれているため、インデックス作成は選択を高速化します。そのため、非クラスター化インデックスを検索すると、ほとんどの場合、クラスター化インデックスから値が読み込まれます。これらの追加の操作により、非クラスター化インデックスはクラスター化インデックスよりも効率が低下します。

これで問題が解決することを願っています。何か意味がわからない場合は、コメントを投稿してください。明確にしようとします。ここではかなり遅れており、私の脳は少し平らに感じています。レッドブルの時間。


質問があります。WHyは、このクエリのCustomerNameの非クラスター化インデックスのインデックスシーク検索です。SELECT* FROM Customer WHERE CustomerName = 'John'。これは非クラスター化インデックスであるため、customernameはソートされません。そのため、インデックススキャンを実行しないでください。
ckv

ところで、上記の質問を除いて、すばらしい答えは完全に理解されました。
ckv

1
インデックスはデータの順序でソートされます。たとえば、インデックス値であるため、顧客名でソートされます。したがって、ソートされます。リーフレベルまたはページをスキャンする必要があることに注意してください。
ナンフィビアン

9

「これは、実際のデータを見つけるために、クエリエンジンが追加の手順を実行する必要があることを意味します。」

必ずしもそうではありません-インデックスが特定のクエリをカバーしている場合、データページにアクセスする必要はありません。また、列が含まれている場合、非クラスター化インデックスに列を追加して、キーサイズを変更せずにカバーすることができます。

したがって、究極の答えは-それは依存します(1つの質問で実際にカバーできるよりもはるかに多くの情報に依存します)-インデックスのすべての機能を理解する必要があり、特定のクエリの実行計画が期待と異なる場合があります。

一般的な経験則として、テーブルには常にクラスター化インデックスがあり(通常はIDまたはシーケンシャルGUIDにあります)、パフォーマンスのために非クラスター化インデックスが追加されます。ただし、常に例外があります。ヒープテーブルには場所があり、より広いクラスター化インデックスには場所があります。ページごとにより多くの行を収めるために狭く見える一見冗長なインデックスには場所があります。などなど

また、許可されているさまざまなインデックスの制限について心配する必要はありません。実際の多くの例では、ほとんど確実に機能しません。


2
+1- there are always exceptionsあまりにも多くの人がこれを省略し、すべてのクラスター化インデックスがint identity何であってもよいと考えています。
JNK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.