Perbedaan kinerja antara Indeks Clustered dan Non Clustered

22

Saya sedang membaca Clustereddan Non Clustered Indexes.

Clustered Index- Ini berisi Halaman Data. Itu berarti informasi baris lengkap akan hadir di Kolom Indeks Clustered.

Non Clustered Index- Ini hanya berisi informasi Row Locator dalam bentuk kolom Indeks Clustered (jika tersedia) atau File Indentifier + Nomor Halaman + Total Baris dalam Halaman. Ini berarti bahwa mesin kueri harus mengambil langkah tambahan untuk menemukan data aktual.

Kueri - Bagaimana saya dapat memeriksa perbedaan kinerja dengan bantuan contoh praktis seperti yang kita ketahui bahwa tabel hanya dapat memiliki satu Clustered Indexdan menyediakan sortingdi Clustered Index Columndan Non Clustered Indextidak menyediakan sortingserta dapat mendukung 999 Non Clustered Indexesin SQL Server 2008dan 249 in SQL Server 2005.


sumber
2
Perbedaan kinerja saat Anda melakukan apa ?, pekerjaan seperti apa yang ingin Anda lakukan dengan meja itu?, Tidak ada solusi tunggal yang sesuai dengan setiap kebutuhan
Lamak
2
Beberapa diskusi nyata di sini mungkin. stackoverflow.com/questions/91688/… stackoverflow.com/questions/5070529/... stackoverflow.com/questions/1251636/... Kita bisa menulis disertasi tentang perbedaan antara indeks yang berkerumun dan yang tidak berkerumun, tapi saya rasa kita tidak akan mengatakan apa pun yang belum tersedia di luar sana untuk Anda baca.
Aaron Bertrand
4
Anda menulis: "Ini berarti bahwa mesin kueri harus mengambil langkah tambahan untuk menemukan data aktual." Sebenarnya, jika semua yang Anda butuhkan adalah kolom yang tercakup dalam indeks , Anda tidak perlu mengambil langkah tambahan setelah Anda menemukan baris target Anda dalam indeks yang tidak tercakup. Hanya ketika Anda membutuhkan kolom yang tidak tercakup oleh indeks nonclustered, SQL Server perlu melakukan pencarian bookmark .
Nick Chammas

Jawaban:

43

Pertanyaan yang sangat bagus karena merupakan konsep yang sangat penting. Ini adalah topik besar dan apa yang akan saya tunjukkan kepada Anda adalah penyederhanaan sehingga Anda dapat memahami konsep dasarnya.

Pertama ketika Anda melihat tabel think index berkerumun . Dalam SQL server jika tabel tidak berisi indeks berkerumun itu adalah tumpukan. Membuat indeks berkerumun di atas meja sebenarnya mengubah tabel menjadi struktur tipe b-tree. Indeks berkerumun Anda adalah tabel Anda tidak terpisah dari tabel

Pernah bertanya-tanya mengapa Anda hanya dapat memiliki satu indeks berkerumun? Nah jika kita memiliki dua indeks berkerumun kita akan membutuhkan dua salinan tabel. Bagaimanapun juga, ini berisi data.

Saya akan mencoba dan menjelaskan ini dengan menggunakan contoh sederhana.

CATATAN: Saya membuat tabel dalam contoh ini dan mengisinya dengan lebih dari 3 juta entri acak. Kemudian jalankan pertanyaan aktual dan tempelkan rencana eksekusi di sini.

Apa yang benar-benar perlu Anda pahami adalah notasi O atau efisiensi operasional . Mari kita asumsikan Anda memiliki tabel berikut.

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]

Jadi di sini kita memiliki tabel dasar dengan kunci berkerumun di CustomerID (Kunci primer dikelompokkan secara default). Dengan demikian tabel disusun / dipesan berdasarkan pada primaryID kunci Pelanggan. Level menengah akan berisi nilai-nilai CustomerID. Halaman data akan berisi seluruh baris sehingga merupakan baris tabel.

Kami juga akan membuat indeks non-cluster di bidang CustomerName. Kode berikut akan melakukannya.

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]

Jadi dalam indeks ini Anda akan menemukan pada halaman data / tingkat daun node pointer ke tingkat menengah dalam indeks berkerumun. Indeks diatur / dipesan di sekitar bidang Nama Pelanggan. Dengan demikian tingkat menengah berisi nilai-nilai CustomerName dan tingkat daun akan berisi pointer (nilai-nilai pointer ini sebenarnya adalah nilai-nilai kunci utama atau kolom CustomerID).

Benar jadi jika kita menjalankan kueri berikut:

SELECT * FROM Customer WHERE CustomerID = 1 

SQL mungkin akan membaca indeks berkerumun melalui operasi pencarian. Operasi pencarian adalah pencarian biner yang jauh lebih efisien daripada scan yang merupakan pencarian berurutan. Jadi dalam contoh kami di atas indeks dibaca dan dengan menggunakan pencarian biner, SQL dapat menghilangkan data yang tidak sesuai dengan kriteria yang kami cari. Lihat tangkapan layar terlampir untuk paket kueri.

masukkan deskripsi gambar di sini

Jadi jumlah operasi atau O Notasi untuk operasi pencarian adalah sebagai berikut:

  1. Lakukan pencarian biner pada indeks berkerumun dengan membandingkan nilai yang dicari dengan nilai-nilai di tingkat menengah.
  2. Mengembalikan nilai yang cocok (ingat karena indeks berkerumun memiliki semua data di dalamnya dapat mengembalikan semua kolom dari indeks karena itu adalah data baris)

Jadi ini adalah dua operasi. Namun jika kami menjalankan kueri berikut:

SELECT * FROM Customer WHERE CustomerName ='John'

SQL sekarang akan menggunakan indeks non-clustered di CustomerName untuk melakukan pencarian. Namun karena ini adalah indeks non-clustered itu tidak berisi semua data di baris.

Jadi SQL akan melakukan pencarian di tingkat menengah untuk menemukan catatan yang cocok kemudian melakukan pencarian menggunakan nilai yang dikembalikan untuk melakukan pencarian lain pada indeks berkerumun (alias tabel) untuk mengambil data aktual. Ini kedengarannya membingungkan, saya tahu tetapi membaca terus dan semuanya akan menjadi jelas.

Karena indeks non-cluster kami hanya berisi bidang CustomerName (nilai-nilai bidang yang diindeks disimpan dalam node perantara) dan penunjuk ke data yang merupakan CustomerID, indeks tidak memiliki catatan Nama Pelanggan. Nama Pelanggan harus diambil dari indeks atau tabel berkerumun.

Saat menjalankan kueri ini, saya mendapatkan paket eksekusi berikut:

masukkan deskripsi gambar di sini

Ada dua hal penting yang perlu Anda perhatikan pada screenshot di atas

  1. SQL mengatakan saya memiliki indeks yang hilang (teks berwarna hijau). SQL menyarankan saya membuat indeks pada CustomerName yang mencakup CustomerID dan CustomerSurname.
  2. Anda juga akan melihat bahwa 99% waktu kueri dihabiskan untuk melakukan pencarian kunci pada indeks kunci utama / indeks berkerumun.

Mengapa SQL menyarankan indeks pada CustomerName lagi? Yah karena indeks hanya berisi CustomerID dan SQL CustomerName masih harus menemukan Nama Pelanggan dari tabel / indeks berkerumun.

Jika kami membuat indeks dan kami menyertakan kolom Nama Pelanggan di indeks SQL akan dapat memenuhi seluruh permintaan dengan hanya membaca indeks non-clustered. Inilah sebabnya mengapa SQL menyarankan saya mengubah indeks non-clustered saya.

Di sini Anda dapat melihat operasi tambahan yang perlu dilakukan SQL untuk mendapatkan kolom Nama Pelanggan dari kunci berkerumun

Dengan demikian jumlah operasi adalah sebagai berikut:

  1. Lakukan pencarian biner pada indeks non-clustered dengan membandingkan nilai yang dicari dengan nilai-nilai di tingkat menengah
  2. Untuk node yang cocok baca node level daun yang akan berisi pointer untuk data dalam indeks berkerumun (node ​​level daun akan berisi nilai kunci primer dengan cara).
  3. Untuk setiap nilai yang dikembalikan, lakukan baca pada indeks berkerumun (tabel) untuk mendapatkan nilai baris di sini kita akan membaca Nama Pelanggan.
  4. Kembalikan baris yang cocok

Itu adalah 4 operasi untuk mendapatkan nilai keluar. Dua kali jumlah operasi yang dibutuhkan dibandingkan dengan membaca indeks berkerumun. Menunjukkan kepada Anda bahwa indeks berkerumun Anda adalah indeks yang paling kuat karena berisi semua data.

Jadi hanya untuk memperjelas satu poin terakhir. Mengapa saya mengatakan bahwa pointer dalam indeks non-cluster adalah nilai kunci primer? Nah untuk menunjukkan bahwa node tingkat daun dari indeks non-clustered berisi nilai kunci primer saya mengubah permintaan saya ke:

SELECT CustomerID
FROM Customer
WHERE CustomerName='Jane'

Dalam kueri ini SQL dapat membaca CustomerID dari indeks non-clustered. Tidak perlu melakukan pencarian pada indeks berkerumun. Ini bisa Anda lihat dari rencana eksekusi yang terlihat seperti ini.

masukkan deskripsi gambar di sini

Perhatikan perbedaan antara permintaan ini dan permintaan sebelumnya. Tidak ada pencarian. SQL dapat menemukan semua data dalam indeks non-cluster

Mudah-mudahan Anda dapat mulai memahami bahwa indeks berkerumun adalah tabel dan indeks non-berkerumun JANGAN berisi semua data. Pengindeksan akan mempercepat pemilihan karena fakta bahwa pencarian biner dapat dilakukan tetapi hanya indeks berkerumun yang berisi semua data. Jadi pencarian pada indeks non-cluster hampir selalu menghasilkan nilai-nilai yang diambil dari indeks cluster. Operasi tambahan ini membuat indeks non-cluster kurang efisien daripada indeks cluster.

Semoga ini jelas. Jika ada yang tidak masuk akal silakan kirim komentar dan saya akan mencoba menjelaskan. Agak terlambat di sini dan otak saya merasa agak datar. Saatnya banteng merah.

Namphibian
sumber
Saya punya pertanyaan. MENGAPA mencari indeks pencarian pada indeks non clustered pada CustomerName untuk permintaan ini SELECT * DARI Pelanggan DI MANA CustomerName = 'John'. Karena ini adalah indeks non-cluster, nama pengguna tidak akan diurutkan. Jadi seharusnya tidak dilakukan pemindaian indeks.
ckv
BTW Jawaban yang bagus benar-benar dipahami kecuali pertanyaan di atas.
ckv
1
Indeks diurutkan dalam urutan data. Misalnya akan diurutkan pada nama Pelanggan karena itu adalah nilai yang diindeks. Jadi disortir. Ingat itu masih harus memindai tingkat daun atau halaman.
Namphibian
9

"Ini berarti bahwa mesin query harus mengambil langkah tambahan untuk menemukan data aktual."

Belum tentu - jika indeks mencakup permintaan yang diberikan, tidak perlu melakukan perjalanan ke halaman data. Juga, dengan kolom yang disertakan, kolom tambahan dapat ditambahkan ke indeks non-cluster untuk membuatnya menutupi tanpa mengubah ukuran kunci.

Jadi jawaban utamanya adalah - Itu Tergantung (pada informasi yang jauh lebih banyak daripada yang dapat Anda bahas dalam satu pertanyaan) - Anda perlu memahami semua kemampuan indeks dan rencana pelaksanaan untuk kueri yang diberikan dapat berbeda dari harapan Anda.

Aturan umum yang saya miliki adalah bahwa tabel selalu memiliki indeks berkerumun (dan biasanya pada identitas atau GUID berurutan), tetapi indeks non-berkerumun ditambahkan untuk kinerja. Tapi selalu ada pengecualian - heap tables memiliki tempat, indeks cluster yang lebih luas punya tempat. Tampaknya indeks yang berlebihan yang lebih sempit agar sesuai dengan lebih banyak baris per halaman ada tempatnya. dll. dll

Dan saya tidak akan khawatir tentang batasan pada berbagai indeks yang diizinkan - itu hampir pasti tidak akan berperan dalam banyak contoh dunia nyata.

Cade Roux
sumber
2
+1 untuk there are always exceptions- terlalu banyak orang mengabaikan ini dan berpikir setiap indeks yang dikelompokkan harus menjadi int identityapa pun yang terjadi.
JNK