Kinerja Indeks Non Clustered pada Heaps vs Indeks Clustered

39

Buku Putih 2007 ini membandingkan kinerja untuk masing-masing pilih / masukkan / hapus / perbarui dan rentang pernyataan pilih pada tabel yang disusun sebagai indeks berkerumun vs yang pada tabel yang disusun sebagai tumpukan dengan indeks yang tidak dikelompokkan pada kolom kunci yang sama dengan CI meja.

Umumnya opsi indeks berkerumun tampil lebih baik dalam tes karena hanya ada satu struktur untuk mempertahankan dan karena tidak perlu untuk pencarian bookmark.

Satu kasus yang berpotensi menarik yang tidak dicakup oleh makalah adalah perbandingan antara indeks yang tidak berkerumun di heap vs indeks yang tidak berkerumun pada indeks berkerumun. Dalam contoh itu saya akan mengharapkan heap bahkan mungkin berkinerja lebih baik karena sekali pada tingkat daun NCI SQL Server memiliki RID untuk mengikuti secara langsung daripada harus melintasi indeks berkerumun.

Adakah yang mengetahui pengujian formal serupa yang telah dilakukan di bidang ini dan jika demikian, apa hasilnya?

Martin Smith
sumber

Jawaban:

41

Untuk memeriksa permintaan Anda, saya membuat 2 tabel berikut skema ini:

  • 7,9 juta catatan mewakili informasi saldo.
  • bidang identitas yang dihitung dari 1 hingga 7,9 juta
  • bidang angka yang mengelompokkan catatan dalam sekitar 500k grup.

Tabel pertama disebut heapmendapat indeks non clustered di lapangan group. Tabel kedua disebut clustmendapat indeks berkerumun di bidang berurutan yang disebut keydan indeks nonclustered di lapangangroup

Pengujian dijalankan pada prosesor I5 M540 dengan 2 core ber-hiphread, memori 4Gb dan windows 64-bit 7.

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)  

Pembaruan pada 9 Mar 2011 : Saya melakukan patokan kedua yang lebih luas dengan menjalankan .net code dan mencatat Durasi, CPU, Reads, Writes, dan RowCounts di Sql Server Profiler. (CommandText yang digunakan akan disebutkan dalam hasil.)

CATATAN: CPU dan Durasi dinyatakan dalam milidetik

  • 1000 pertanyaan
  • nol permintaan CPU dihilangkan dari hasil
  • 0 baris yang terpengaruh dihilangkan dari hasil
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));
                        }
                    }
                }
            }

Akhir Pembaruan pada 9 Mar 2011 .

Performa SELECT

Untuk memeriksa angka performansi, saya melakukan kueri berikut di atas tabel heap dan sekali di atas tabel clust:

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

Hasil dari tolok ukur ini adalah untuk 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

Pembaruan pada 9 Mar 2011 : cmd.CommandText = "select * from heap where group between @id and @id+1000";

  • 721 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris
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

Akhir Pembaruan pada 9 Mar 2011 .


untuk tabel clusthasilnya adalah:

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

Pembaruan pada 9 Mar 2011 : cmd.CommandText = "select * from clust where group between @id and @id+1000";

  • 721 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris
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

Akhir Pembaruan pada 9 Mar 2011 .


PILIH DENGAN BERGABUNG kinerja

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


Hasil dari tolok ukur ini adalah untuk heap:

873 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris

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

Hasil dari tolok ukur ini adalah untuk clust:

865 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris

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

MEMPERBARUI kinerja

Kumpulan pertanyaan kedua adalah pernyataan pembaruan:

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

hasil tolok ukur ini untuk 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

Pembaruan pada 9 Mar 2011 : cmd.CommandText = "update heap set amount = amount + @id where group between @id and @id+1000";

  • 811 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris
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

Akhir Pembaruan pada 9 Mar 2011 .


hasil tolok ukur ini untuk 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

Pembaruan pada 9 Mar 2011 : cmd.CommandText = "update clust set amount = amount + @id where group between @id and @id+1000";

  • 853 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris
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

Akhir Pembaruan pada 9 Mar 2011 .


HAPUS tolok ukur

kumpulan ketiga kueri yang saya jalankan adalah menghapus pernyataan

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

Hasil patokan ini untuk 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

Pembaruan pada 9 Mar 2011 : cmd.CommandText = "delete heap where group between @id and @id+1000";

  • 724 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris
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

Akhir Pembaruan pada 9 Mar 2011 .


hasil patokan ini untuk 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

Pembaruan pada 9 Mar 2011 :

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

  • 751 Baris memiliki> 0 CPU dan memengaruhi lebih dari 0 baris
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

Akhir Pembaruan pada 9 Mar 2011 .


Masukkan tolok ukur

Bagian terakhir dari tolok ukur adalah pelaksanaan pernyataan insert.

masukkan nilai heap / clust (...) (...), (...), (...), (...), (...), (...)


Hasil patokan ini untuk heap:

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

Pembaruan pada 9 Mar 2011 :

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 pernyataan memiliki> 0 CPU
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

Akhir Pembaruan pada 9 Mar 2011 .


Hasil patokan ini untuk clust:

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

Pembaruan pada 9 Mar 2011 :

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 pernyataan memiliki> 0 CPU
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

Akhir Pembaruan pada 9 Mar 2011 .


Kesimpulan

Meskipun ada lebih banyak pembacaan logis yang terjadi ketika mengakses tabel dengan clustered & indeks nonclustered (saat menggunakan indeks nonclustered) hasil kinerja adalah:

  • Pernyataan SELECT dapat dibandingkan
  • Pernyataan UPDATE lebih cepat dengan indeks berkerumun di tempat
  • HAPUS pernyataan lebih cepat dengan indeks berkerumun di tempat
  • Pernyataan INSERT lebih cepat dengan indeks berkerumun di tempat

Tentu saja tolok ukur saya sangat terbatas pada jenis tabel tertentu dan dengan serangkaian pertanyaan yang sangat terbatas, tetapi saya pikir berdasarkan informasi ini kita sudah dapat mulai mengatakan bahwa sebenarnya selalu lebih baik untuk membuat indeks berkerumun di meja Anda.

Pembaruan pada 9 Mar 2011 :

Seperti yang dapat kita lihat dari hasil yang ditambahkan, kesimpulan pada tes terbatas tidak benar dalam setiap kasus.

Durasi tertimbang

Hasilnya sekarang menunjukkan bahwa satu-satunya pernyataan yang mendapat manfaat dari indeks berkerumun adalah pernyataan pembaruan. Pernyataan lainnya sekitar 30% lebih lambat di atas meja dengan indeks berkerumun.

Beberapa grafik tambahan tempat saya merencanakan durasi tertimbang per kueri untuk heap vs clust. Tumpukan Durasi Durasi vs dikelompokkan untuk Pilih

Tumpukan Durasi Durasi vs dikelompokkan untuk Bergabung

Tumpukan Durasi Durasi vs dikelompokkan untuk Pembaruan

Tumpukan Durasi Durasi vs dikelompokkan untuk Hapus

Seperti yang Anda lihat profil kinerja untuk memasukkan pernyataan cukup menarik. Paku disebabkan oleh beberapa titik data yang membutuhkan waktu lebih lama untuk diselesaikan. Tumpukan Durasi Durasi vs dikelompokkan untuk Sisipan

Akhir Pembaruan pada 9 Mar 2011 .

Filip De Vos
sumber
@ Martin Saya akan mencoba menjalankan ini di server dengan beberapa tabel dengan 500 juta catatan ketika saya menemukan waktu minggu depan.
Filip De Vos
Saya meragukan kebenaran tes ini. Beberapa bagian memerlukan perhatian serius, seperti kinerja INSERT yang mengklaim indeks yang dikelompokkan lebih cepat - ada lebih banyak bacaan dalam versi CLUST, tetapi waktu yang berlalu lebih sedikit. Saya pribadi akan mengabaikan waktu yang telah berlalu dalam 10 detik dari milidetik (variabilitas waktu) - itu berarti kurang dari jumlah baca.
Lihat Debat Indeks Clustered Kimberly Tripp Berlanjut di mana dia menjelaskan mengapa sebagian besar (jika tidak semua) operasi dengan tabel berkerumun lebih cepat daripada dengan tumpukan - beberapa bertentangan dengan hasil Anda ...
marc_s
1
@Martin, @Richard, @marc_s. Saya sedang mengerjakan patokan yang lebih serius sekarang. Saya berharap dapat menambahkan hasilnya nanti hari ini.
Filip De Vos
1
@Filip - Wow! Anda tentu pantas mendapatkan hadiah untuk semua kerja keras yang telah Anda masukkan ke dalam jawaban ini. Meskipun seperti yang Anda tunjukkan dengan tepat, ini adalah salah satu tolok ukur pada jenis meja tertentu dengan seperangkat kueri dan jarak tempuh yang sangat terbatas pasti bervariasi.
Martin Smith
12

Ketika Kimberly Tripp - Ratu Pengindeksan - menjelaskan dengan sangat baik di posting blognya The Clustered Index Debate berlanjut ... , memiliki kunci pengelompokan pada tabel basis data mempercepat semua operasi - bukan hanya SELECT.

SELECT umumnya lebih lambat pada tumpukan dibandingkan dengan tabel berkerumun, selama Anda memilih kunci pengelompokan yang baik - sesuatu seperti INT IDENTITY. Jika Anda menggunakan kunci pengelompokan yang benar-benar buruk, seperti GUID atau kunci majemuk dengan banyak komponen panjang variabel, maka, tetapi hanya kemudian, tumpukan mungkin lebih cepat. Tetapi dalam hal ini, Anda benar-benar perlu membersihkan desain basis data Anda di tempat pertama ...

Jadi secara umum, saya tidak berpikir ada gunanya tumpukan - pilih kunci pengelompokan yang baik dan berguna dan Anda harus mendapat manfaat dalam semua hal.

marc_s
sumber
3
Ini bukan jawaban. Martin cukup solid di SQL Server; pertanyaan itu dimaksudkan untuk mendapatkan hasil diverifikasi dunia nyata diverifikasi dari pengujian kinerja, bukan teori lagi.
Artikel Kimberly Tripp yang ditautkan secara efektif mengasumsikan semua indeks tidak tercakup dilindungi. Jika itu masalahnya, maka tidak akan ada pencarian, dan keuntungan tumpukan di pencarian akan dinegasikan. Namun, itu bukan dunia yang kebanyakan dari kita hidup. Dalam kasus kami, mencoba mendesain semua atau sebagian besar indeks tidak tercakup kami untuk menciptakan masalah sendiri.
@ dbaguy52: menurut Anda mengapa Kim Tripp menganggap semua indeks NC mencakup? Saya tidak melihat setiap gagasan yang dalam dirinya blog posting ..... tolong jelaskan lebih detail apa yang membuat Anda percaya itu yang terjadi (atau yang asumsi nya)
marc_s
7

Kebetulan menemukan artikel ini dari Joe Chang yang membahas pertanyaan ini. Disisipkan kesimpulannya di bawah ini.

Pertimbangkan tabel yang memiliki indeks kedalaman 4, sehingga ada tingkat akar, 2 tingkat menengah dan tingkat daun. Pencarian indeks untuk kunci indeks tunggal (yaitu, tidak ada pencarian kunci) akan menghasilkan 4 IO logis (LIO). Sekarang pertimbangkan jika pencarian kunci diperlukan. Jika tabel memiliki indeks berkerumun juga dari kedalaman 4, setiap pencarian kunci menghasilkan 4 LIO. Jika tabel adalah tumpukan, setiap pencarian kunci menghasilkan 1 LIO. Pada kenyataannya, pencarian kunci untuk tumpukan adalah sekitar 20-30% lebih murah daripada pencarian kunci untuk indeks berkerumun, tidak di mana pun dekat dengan rasio LIO 4: 1.

Martin Smith
sumber
1
Hal yang menarik untuk dicatat adalah bahwa kutipan dari Joe Chang mengidentifikasi keunggulan efisiensi 20-30% untuk tumpukan berdasarkan asumsi yang merupakan keuntungan yang hampir sama diidentifikasi dalam pembaruan 9 Maret untuk artikel.