ヒープ上の非クラスター化インデックスとクラスター化インデックスのパフォーマンス


39

この2007年ホワイトペーパーでは、クラスター化インデックスとして構成されたテーブルと、CIと同じキー列に非クラスター化インデックスを備えたヒープとして構成されたテーブルの個々の選択/挿入/削除/更新および範囲選択ステートメントのパフォーマンスを比較しています表。

通常、クラスター化インデックスオプションは、維持する構造が1つだけであり、ブックマークの参照が不要なため、テストでのパフォーマンスが向上しました。

この論文で取り上げられていない興味深いケースの1つは、ヒープ上の非クラスター化インデックスとクラスター化インデックス上の非クラスター化インデックスの比較です。その場合、NCIリーフレベルでSQL Serverがクラスター化インデックスをトラバースする必要がなく、直接従うRIDを持っているため、ヒープのパフォーマンスがさらに向上することを期待していました。

この分野で行われた同様の正式なテストを知っている人はいますか?

回答:


41

リクエストを確認するために、このスキームに従って2つのテーブルを作成しました。

  • 残高情報を表す790万件のレコード。
  • 1から790万までカウントするIDフィールド
  • レコードを約50万グループにグループ化する数値フィールド。

最初に呼び出されたテーブルはheap、フィールドに非クラスター化インデックスを取得しましたgroup。呼び出された2番目のテーブルclustは、呼び出されたシーケンシャルフィールドのクラスター化インデックスと、フィールドkeyの非クラスター化インデックスを取得しましたgroup

テストは、2つのハイパースレッドコア、4Gbメモリ、64ビットウィンドウ7を備えたI5 M540プロセッサで実行されました。

Microsoft SQL Server 2008 R2 (RTM) - 10.50.1600.1 (X64) 
Apr  2 2010 15:48:46 
Developer Edition (64-bit) on Windows NT 6.1 <X64> (Build 7601: Service Pack 1)  

2011年3月9日に更新:次の.netコードを実行し、Sql Server ProfilerでDuration、CPU、読み取り、書き込み、およびRowCountsを記録することにより、2番目のより広範なベンチマークを行いました。(使用されるCommandTextは結果に記載されます。)

注: CPUと期間はミリ秒で表されます

  • 1000件のクエリ
  • ゼロCPUクエリは結果から削除されます
  • 影響を受けた0行は結果から削除されます
int[] idList = new int[] { 6816588, 7086702, 6498815 ... }; // 1000 values here.
using (var conn = new SqlConnection(@"Data Source=myserver;Initial Catalog=mydb;Integrated Security=SSPI;"))
            {
                conn.Open();
                using (var cmd = new SqlCommand())
                {
                    cmd.Connection = conn;
                    cmd.CommandType = CommandType.Text;
                    cmd.CommandText = "select * from heap where common_key between @id and @id+1000"; 
                    cmd.Parameters.Add("@id", SqlDbType.Int);
                    cmd.Prepare();
                    foreach (int id in idList)
                    {
                        cmd.Parameters[0].Value = id;

                        using (var reader = cmd.ExecuteReader())
                        {
                            int count = 0;
                            while (reader.Read())
                            {
                                count++;
                            }
                            Console.WriteLine(String.Format("key: {0} => {1} rows", id, count));
                        }
                    }
                }
            }

2011年3月9日の更新の終了

SELECTパフォーマンス

パフォーマンス番号を確認するために、ヒープテーブルとclustテーブルで次のクエリを1回実行しました。

select * from heap/clust where group between 5678910 and 5679410
select * from heap/clust where group between 6234567 and 6234967
select * from heap/clust where group between 6455429 and 6455729
select * from heap/clust where group between 6655429 and 6655729
select * from heap/clust where group between 6955429 and 6955729
select * from heap/clust where group between 7195542 and 7155729

このベンチマークの結果は次のとおりですheap

rows  reads CPU   Elapsed 
----- ----- ----- --------
1503  1510  31ms  309ms
401   405   15ms  283ms
2700  2709  0ms   472ms
0     3     0ms   30ms
2953  2962  32ms  257ms
0     0     0ms   0ms

2011年3月9日に更新cmd.CommandText = "select * from heap where group between @id and @id+1000";

  • 721行にはCPUが0を超え、0行を超える行に影響します
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    6368         -         
Cpu            15        374      37   0.00754
Reads        1069      91459    7682   1.20155
Writes          0          0       0   0.00000
Duration   0.3716   282.4850 10.3672   0.00180

2011年3月9日の更新の終了


clustの結果は次のとおりです。

rows  reads CPU   Elapsed 
----- ----- ----- --------
1503  4827  31ms  327ms
401   1241  0ms   242ms
2700  8372  0ms   410ms
0     3     0ms   0ms
2953  9060  47ms  213ms
0     0     0ms   0ms

2011年3月9日に更新cmd.CommandText = "select * from clust where group between @id and @id+1000";

  • 721行にはCPUが0を超え、0行を超える行に影響します
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    6056         -
Cpu            15        468      38   0.00782
Reads        3194     227018   20457   3.37618
Writes          0          0       0       0.0
Duration   0.3949   159.6223 11.5699   0.00214

2011年3月9日の更新の終了


SELECT WITH JOINパフォーマンス

cmd.CommandText = "select * from heap/clust h join keys k on h.group = k.group where h.group between @id and @id+1000";


このベンチマークの結果は次のとおりですheap

873行のCPUは0を超えており、0行を超える行に影響します

Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1009       4170    1683         -
Cpu            15         47      18   0.01175
Reads        2145       5518    2867   1.79246
Writes          0          0       0   0.00000
Duration   0.8215   131.9583  1.9095   0.00123

このベンチマークの結果は次のとおりですclust

865行のCPUが0を超えており、0行を超える行に影響します

Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1000       4143    1685         -
Cpu            15         47      18   0.01193
Reads        5320      18690    8237   4.97813
Writes          0          0       0   0.00000
Duration   0.9699    20.3217  1.7934   0.00109

更新パフォーマンス

クエリの2番目のバッチは更新ステートメントです。

update heap/clust set amount = amount + 0 where group between 5678910 and 5679410
update heap/clust set amount = amount + 0 where group between 6234567 and 6234967
update heap/clust set amount = amount + 0 where group between 6455429 and 6455729
update heap/clust set amount = amount + 0 where group between 6655429 and 6655729
update heap/clust set amount = amount + 0 where group between 6955429 and 6955729
update heap/clust set amount = amount + 0 where group between 7195542 and 7155729

このベンチマークの結果heap

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  3013  31ms  175ms
401   806   0ms   22ms
2700  5409  47ms  100ms
0     3     0ms   0ms
2953  5915  31ms  88ms
0     0     0ms   0ms

2011年3月9日に更新cmd.CommandText = "update heap set amount = amount + @id where group between @id and @id+1000";

  • 811 CPUに0を超える行があり、0を超える行に影響します
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    5598       811         
Cpu            15        873      56   0.01199
Reads        2080     167593   11809   2.11217
Writes          0       1687     121   0.02170
Duration   0.6705   514.5347 17.2041   0.00344

2011年3月9日の更新の終了


このベンチマークの結果clust

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  9126  16ms  35ms
401   2444  0ms   4ms
2700  16385 31ms  54ms
0     3     0ms   0ms 
2953  17919 31ms  35ms
0     0     0ms   0ms

2011年3月9日に更新cmd.CommandText = "update clust set amount = amount + @id where group between @id and @id+1000";

  • 853行にはCPUが0を超えており、0行を超える行に影響します
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1001      69788    5420         -
Cpu            15        594      50   0.01073
Reads        6226     432237   33597   6.20450
Writes          0       1730     110   0.01971
Duration   0.9134   193.7685  8.2919   0.00155

2011年3月9日の更新の終了


ベンチマークの削除

私が実行したクエリの3番目のバッチは削除ステートメントです

delete heap/clust where group between 5678910 and 5679410
delete heap/clust where group between 6234567 and 6234967
delete heap/clust where group between 6455429 and 6455729
delete heap/clust where group between 6655429 and 6655729
delete heap/clust where group between 6955429 and 6955729
delete heap/clust where group between 7195542 and 7155729

このベンチマークの結果heap

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  10630 62ms  179ms
401   2838  0ms   26ms
2700  19077 47ms  87ms
0     4     0ms   0ms
2953  20865 62ms  196ms
0     4     0ms   9ms

2011年3月9日に更新cmd.CommandText = "delete heap where group between @id and @id+1000";

  • 724行のCPUが0を超えており、0行を超える行に影響する
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts     192      69788    4781         -
Cpu            15        499      45   0.01247
Reads         841     307958   20987   4.37880
Writes          2       1819     127   0.02648
Duration   0.3775  1534.3383 17.2412   0.00349

2011年3月9日の更新の終了


このベンチマークの結果clust

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
1503  9228  16ms  55ms
401   3681  0ms   50ms
2700  24644 46ms  79ms
0     3     0ms   0ms
2953  26955 47ms  92ms
0     3     0ms   0ms

2011年3月9日に更新

cmd.CommandText = "delete clust where group between @id and @id+1000";

  • 751行にはCPUが0を超えており、0行を超える行に影響します
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts     144      69788    4648         -
Cpu            15        764      56   0.01538
Reads         989     458467   30207   6.48490
Writes          2       1830     127   0.02694
Duration   0.2938  2512.1968 24.3714   0.00555

2011年3月9日の更新の終了


ベンチマークの挿入

ベンチマークの最後の部分は、挿入ステートメントの実行です。

ヒープ/クレストに挿入(...)値(...)、(...)、(...)、(...)、(...)、(...)


このベンチマークの結果heap

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
6     38    0ms   31ms

2011年3月9日に更新

string str = @"insert into heap (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
                    values";

                    for (int x = 0; x < 999; x++)
                    {
                        str += string.Format(@"(@id + {0}, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'),  ", x);
                    }
                    str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);

                    cmd.CommandText = str;
  • 912ステートメントのCPUが0より大きい
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1000       1000    1000         -
Cpu            15       2138      25   0.02500
Reads        5212       7069    6328   6.32837
Writes         16         34      22   0.02222
Duration   1.6336   293.2132  4.4009   0.00440

2011年3月9日の更新の終了


このベンチマークの結果clust

rows  reads CPU   Elapsed 
----- ----- ----- -------- 
6     50    0ms   18ms

2011年3月9日に更新

string str = @"insert into clust (group, currency, year, period, domain_id, mtdAmount, mtdAmount, ytdAmount, amount, ytd_restated, restated, auditDate, auditUser)
                    values";

                    for (int x = 0; x < 999; x++)
                    {
                        str += string.Format(@"(@id + {0}, 'EUR', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test'),  ", x);
                    }
                    str += string.Format(@"(@id, 'CAD', 2012, 2, 0, 100, 100, 1000 + @id,1000, 1000,1000, current_timestamp, 'test') ", 1000);

                    cmd.CommandText = str;
  • 946ステートメントのCPUが0より大きい
Counter   Minimum    Maximum Average  Weighted
--------- ------- ---------- ------- ---------
RowCounts    1000       1000    1000         -      
Cpu            15       2403      21   0.02157
Reads        6810       8997    8412   8.41223
Writes         16         25      19   0.01942
Duration   1.5375   268.2571  6.1463   0.00614

2011年3月9日の更新の終了


結論

クラスター化インデックスと非クラスター化インデックスを使用してテーブルにアクセスする場合(非クラスター化インデックスを使用している場合)に論理読み取りが行われますが、パフォーマンスの結果は次のとおりです。

  • SELECTステートメントは比較可能です
  • クラスター化インデックスを配置すると、UPDATEステートメントが高速になります
  • DELETEステートメントは、クラスター化インデックスが配置されている方が高速です
  • INSERTステートメントは、クラスター化インデックスが適切に配置されている方が高速です

もちろん、私のベンチマークは特定の種類のテーブルとクエリの非常に限られたセットで非常に制限されていましたが、この情報に基づいて、テーブルにクラスター化インデックスを作成することは事実上常に良いと既に言い始めることができると思います。

2011年3月9日に更新

追加された結果からわかるように、限定されたテストに関する結論は、すべてのケースで正しいとは限りませんでした。

加重期間

結果は、クラスター化インデックスの恩恵を受ける唯一のステートメントが更新ステートメントであることを示しています。他のステートメントは、クラスター化インデックスを使用したテーブルでは約30%遅くなります。

ヒープとclustのクエリごとの加重期間をプロットした追加のグラフ。 加重期間ヒープと選択用のクラスター化

加重持続時間ヒープと結合のクラスター化

更新のためのクラスタ化された加重期間ヒープ

加重持続時間ヒープと削除のクラスター化

ご覧のとおり、挿入ステートメントのパフォーマンスプロファイルは非常に興味深いものです。スパイクは、完了までにかなり長い時間がかかるいくつかのデータポイントによって引き起こされます。 加重持続時間ヒープと挿入のクラスター化

2011年3月9日の更新の終了


@Martin来週に時間があれば、5億レコードのテーブルをいくつか持つサーバーでこれを実行しようとします。
フィリップデヴォス

このテストの真実性を疑います。クラスター化インデックスの方が高速であると主張するINSERTパフォーマンスなど、一部の部分は真剣に注意する必要があります-CLUSTバージョンでは読み取りが多くなりましたが、経過時間は短くなります。私は個人的には、経過時間が10ミリ秒以内であることを無視していました(タイミングのばらつき)-これは読み取りカウントより少ないことを意味します。

チェックアウトキンバリーメーカーTrippのザ・クラスタ化インデックス討論が続くクラスタ化されたテーブルと(すべてではない)ほとんどの操作が速く、ヒープとよりも、なぜ彼女は説明している-あなたの結果にいくつか反して...
marc_s

1
@ Martin、@ Richard、@ marc_s 私は現在、より深刻なベンチマークに取り組んでいます。今日結果を追加できることを願っています。
フィリップデヴォス

1
@Filip-わあ!あなたは間違いなく、あなたがこの答えに費やしたすべての懸命な努力に対する報いに値します。ご指摘のとおり、これは特定の種類のテーブルのベンチマークであり、クエリのセットは非常に限られており、燃費は間違いなく異なります。
マーティンスミス

12

インデックスの女王- -キンバリーメーカーTrippとして非常にうまく、彼女はポストのブログで説明してザ・クラスタ化インデックスディベートを...続けてかなりのスピードアップ、データベーステーブル上のクラスタ化キーを持つ、すべての操作-だけではなくSELECT

SELECTは、一般的に、クラスター化されたテーブルと比較して、適切なクラスタリングキーを選択する限り、ヒープ上では低速ですINT IDENTITY。GUIDや、可変長コンポーネントが多数含まれる複合キーなど、本当に悪いクラスタリングキーを使用する場合、その場合のみ、ヒープが高速になる可能性があります。しかし、その場合、まずデータベース設計をクリーンアップする必要があります...

したがって、一般的に、ヒープにポイントはないと思います。適切で便利なクラスタリングキーを選択すれば、あらゆる点でメリットがあります。


3
これは無回答です。MartinはSQL Serverでかなり堅実です。質問は、理論ではなく、パフォーマンステストから実際のテスト済みの検証結果を取得することを目的としていました。

リンクされたKimberly Trippの記事は、すべての非クラスター化インデックスがカバーしていると効果的に想定しています。その場合、ルックアップは行われず、ルックアップでのヒープの利点は無効になります。しかし、それは私たちのほとんどが住んでいる世界ではありません。私たちの場合、カバーする非クラスター化インデックスのすべてまたはほとんどを設計しようとすると、独自の問題が発生します。

@ dbaguy52:キム・トリップがすべてのNCインデックスがカバーしていると仮定するのはなぜだと思いますか?私は表示されない任意のあなたはそれが事実だと信じて(または彼女の前提だという)にするものをより詳細に説明してください.....彼女のブログでその概念を
marc_s

7

たまたま、この質問に対処するJoe Changからのこの記事に出くわしました。以下に彼の結論を貼り付けた。

ルートレベル、2つの中間レベル、およびリーフレベルがあるように、インデックスの深さが4であるテーブルを考えます。単一のインデックスキーのインデックスシーク(つまり、キールックアップなし)は、4つの論理IO(LIO)を生成します。次に、キー検索が必要かどうかを検討します。テーブルに深さ4のクラスター化インデックスがある場合、各キールックアップは4つのLIOを生成します。テーブルがヒープの場合、各キールックアップは1つのLIOを生成します。実際には、ヒープへのキールックアップは、クラスター化インデックスへのキールックアップよりも約20〜30%安く、4:1のLIO比に近い場所ではありません。


1
注目すべき興味深い点は、ジョーチャンからの引用が、彼の仮定に基づいてヒープの効率が20〜30%向上したことを示していることです。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.