Seberapa pentingkah urutan kolom dalam indeks?

173

Saya pernah mendengar bahwa Anda harus meletakkan kolom yang paling selektif di awal deklarasi indeks. Contoh:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

Pertama, apakah yang saya katakan benar? Jika demikian, apakah saya cenderung melihat perbedaan besar dalam kinerja dengan menata ulang urutan kolom dalam indeks saya atau apakah ini lebih merupakan praktik "menyenangkan untuk dilakukan"?

Alasan saya bertanya adalah karena setelah memasukkan kueri melalui DTA, saya merekomendasikan agar saya membuat indeks yang memiliki hampir semua kolom yang sama di dalamnya sebagai indeks yang ada, hanya dalam urutan yang berbeda. Saya sedang mempertimbangkan hanya menambahkan kolom yang hilang ke indeks yang ada dan menyebutnya baik. Pikiran?

Abe Miessler
sumber

Jawaban:

193

Lihatlah indeks seperti ini:

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

Lihat bagaimana membatasi A pertama, karena kolom pertama Anda menghilangkan lebih banyak hasil daripada membatasi pada kolom kedua Anda terlebih dahulu? Lebih mudah jika Anda membayangkan bagaimana indeks harus dilintasi, kolom 1, lalu kolom 2, dll ... Anda melihat bahwa memangkas sebagian besar hasil dalam kepalan tangan membuat langkah ke-2 jauh lebih cepat.

Kasus lain, jika Anda bertanya pada kolom 3, pengoptimal bahkan tidak akan menggunakan indeks, karena itu sama sekali tidak membantu dalam mempersempit set hasil. Kapan pun Anda berada dalam kueri, persempit jumlah hasil yang akan ditangani sebelum langkah berikutnya berarti kinerja yang lebih baik.

Karena indeks juga disimpan dengan cara ini, tidak ada kemunduran di seluruh indeks untuk menemukan kolom pertama ketika Anda menanyakannya.

Singkatnya: Tidak, ini bukan untuk pertunjukan, ada manfaat kinerja nyata.

Nick Craver
sumber
13
Pada gambar di atas, perlu diingat bahwa indeks itu hanya akan bermanfaat jika Kolom 1 ditentukan dalam kueri. Jika kueri Anda hanya menentukan Kolom 2 di Gabung atau Cari Predikat, maka itu tidak akan bermanfaat. Jadi ketertiban juga penting di sana. Mungkin itu tidak perlu dikatakan, tetapi ingin menyebutkannya.
CodeCowboyOrg
3
Juga perlu diingat, misalkan Indeks Anda seperti gambar di atas, dan permintaan Anda memfilter pada kolom1 dan kolom2, tetapi kolom2 lebih unik dan apa yang sebenarnya ingin Anda filter sebenarnya adalah kolom2, maka lebih menguntungkan jika hanya memiliki indeks di mana kolom 2 adalah yang pertama. Ini mungkin tampak berlawanan dengan intuisi tetapi perlu diingat bahwa indeks disimpan pada beberapa halaman dan merupakan pohon dengan rentang nilai, sedangkan Kolom 1 di atas meniadakan 1/2 kemungkinan, indeks sudah tahu halaman indeks mana yang akan langsung menuju untuk Nilai Column2, tidak perlu Kolom 1 untuk mempersempit himpunan.
CodeCowboyOrg
4
Gambar ini bukan representasi akurat tentang bagaimana indeks disusun atau dinavigasi. Telah mengirimkan jawaban yang memperbaiki stackoverflow.com/a/39080819/73226
Martin Smith
6
@ MartinSmith Saya tidak setuju bahwa itu tidak akurat. Sangat diakui sangat disederhanakan, yang merupakan maksud saya. Namun, jawaban Anda yang menggali lebih dalam tentang level sangat dihargai, bagi mereka yang ingin menggali lebih dalam. Jika Anda melihat gambar pohon Anda, Anda akan melihat apa yang saya ilustrasikan dengan cara yang sangat sederhana. Ini tidak terlalu unik atau bahkan SQL khusus; Pengindeksan B-tree cukup umum di banyak hal.
Nick Craver
@ MartinSmith Saya juga tidak setuju bahwa itu tidak akurat, apa yang Anda uraikan adalah perilaku standar tentang cara mencapai indeks penutup - selektivitas jauh lebih penting setelah Anda melakukan kueri rentang karena ini meminimalkan jumlah halaman indeks yang optimizer harus memindai; ini bisa signifikan dalam tabel besar dengan jutaan baris
Paul Hatcher
127

Urutan kolom sangat penting. Sekarang urutan mana yang benar itu tergantung pada bagaimana Anda akan menanyakannya. Indeks dapat digunakan untuk melakukan pencarian yang tepat atau pemindaian rentang. Pencarian tepat adalah ketika nilai untuk semua kolom dalam indeks ditentukan dan kueri mendarat tepat pada baris yang diinginkan. Untuk mencari urutan kolom tidak relevan. Pemindaian rentang adalah ketika hanya beberapa kolom yang ditentukan, dan dalam hal ini ketika urutan menjadi penting. SQL Server dapat menggunakan indeks untuk pemindaian rentang hanya jika kolom paling kiri ditentukan, dan kemudian hanya jika kolom paling kiri berikutnya ditentukan, dan seterusnya. Jika Anda memiliki indeks pada (A, B, C) dapat digunakan untuk rentang pemindaian A=@a, untuk A=@a AND B=@btetapi tidak untuk B=@b, untuk C=@catau B=@b AND C=@c. Kasing A=@a AND C=@cdicampur satu, seperti dalamA=@aporsi akan menggunakan indeks, tetapi C=@ctidak (kueri akan memindai semua nilai B untuk A=@a, tidak akan 'lewati' ke C=@c). Sistem basis data lain memiliki apa yang disebut operator 'skip scan' yang dapat mengambil keuntungan dari kolom dalam dalam indeks ketika kolom luar tidak ditentukan.

Dengan pengetahuan itu di tangan Anda dapat melihat definisi indeks lagi. Indeks aktif hanya (MostSelective, SecondMost, Least)akan efektif bila MostSelectivekolom ditentukan. Tapi itu yang paling selektif, relevansi kolom batin akan cepat menurun. Sangat sering Anda akan menemukan bahwa indeks yang lebih baik aktif (MostSelective) include (SecondMost, Least)atau aktif (MostSelective, SecondMost) include (Least). Karena kolom dalam kurang relevan, menempatkan kolom selektivitas rendah di posisi yang tepat dalam indeks membuat mereka tidak lebih dari derau untuk pencarian, jadi masuk akal untuk memindahkannya dari halaman perantara dan menyimpannya hanya di halaman daun, untuk tujuan cakupan permintaan. Dengan kata lain, pindahkan ke TERMASUK. Ini menjadi lebih penting karena ukuran Leastkolom bertambah. Idenya adalah bahwa indeks ini hanya dapat menguntungkan kueri yang menentukanMostSelective baik sebagai nilai yang tepat atau rentang, dan kolom yang paling selektif itu sudah membatasi baris kandidat.

Di sisi lain indeks pada (Least, SecondMost, MostSelective)mungkin tampak kesalahan, tetapi sebenarnya indeks yang cukup kuat. Karena memiliki Leastkolom sebagai kueri terluarnya, ia dapat digunakan untuk kueri yang harus mengumpulkan hasil pada kolom selektivitas rendah. Pertanyaan seperti itu lazim di OLAP dan analisis gudang data, dan di sinilah tepatnya indeks tersebut memiliki kasus yang sangat baik untuk mereka. Indeks tersebut sebenarnya membuat indeks berkerumun sangat baik , tepatnya karena mereka mengatur tata letak fisik pada potongan besar dari baris terkait ( Leastnilai yang sama , yang biasanya menunjukkan semacam kategori atau jenis) dan mereka memfasilitasi permintaan analisis.

Jadi, sayangnya, tidak ada pesanan yang 'benar'. Anda tidak boleh mengikuti resep pemotong kue apa pun melainkan menganalisis pola kueri yang akan Anda gunakan terhadap tabel itu dan memutuskan urutan kolom indeks mana yang benar.

Remus Rusanu
sumber
3
Respon luar biasa seperti biasa. Saya akan membaca paragraf ketiga Anda beberapa kali lagi dan menindaklanjutinya. Saya menduga mungkin itulah yang harus saya lakukan.
Abe Miessler
"SQL Server dapat menggunakan indeks untuk pemindaian rentang hanya jika kolom paling kiri ditentukan, dan kemudian hanya jika kolom paling kiri berikutnya ditentukan, dan seterusnya." Inilah yang sebenarnya hilang dari pemahaman saya, terima kasih! Saya tidak tahu pemindaian rentang hanya dapat dilakukan pada kolom indeks yang paling kanan digunakan, tetapi sekarang saya melakukannya dengan sangat masuk akal.
Allon Guralnek
Apakah penjelasan ini berlaku untuk Oracle DB?
lain
1
@Roizpi Ya, pada dasarnya setiap hubungan database dengan Indeks berfungsi dengan cara yang sama atau sangat mirip.
Tatranskymedved
45

Seperti yang dikatakan Remus tergantung pada beban kerja Anda.

Saya ingin membahas aspek yang menyesatkan dari jawaban yang diterima.

Untuk kueri yang melakukan pencarian kesetaraan pada semua kolom dalam indeks, tidak ada perbedaan yang signifikan.

Di bawah ini membuat dua tabel dan mengisinya dengan data yang identik. Satu-satunya perbedaan adalah bahwa salah satu memiliki kunci yang dipesan dari yang paling selektif dan yang lainnya terbalik.

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

Sekarang melakukan kueri terhadap kedua tabel ...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

... Keduanya menggunakan denda indeks dan keduanya diberi biaya yang sama persis.

masukkan deskripsi gambar di sini

Seni ASCII dalam jawaban yang diterima sebenarnya bukan bagaimana indeks disusun. Halaman indeks untuk Table1 diwakili di bawah ini (klik gambar untuk membuka dalam ukuran penuh).

masukkan deskripsi gambar di sini

Halaman indeks berisi baris yang berisi seluruh kunci (dalam hal ini sebenarnya ada kolom kunci tambahan yang ditambahkan untuk pengidentifikasi baris karena indeks tidak dinyatakan sebagai unik tetapi yang dapat diabaikan informasi lebih lanjut tentang ini dapat ditemukan di sini ).

Untuk kueri di atas SQL Server tidak peduli dengan selektivitas kolom. Itu melakukan pencarian biner dari halaman root dan menemukan bahwa Key (PPP...,3,~ ) adalah >=(JJJ...,1,~ )dan < (SSS...,3,~ )karena itu harus membaca halaman 1:118. Kemudian melakukan pencarian biner dari entri kunci pada halaman itu dan menemukan halaman daun untuk melakukan perjalanan.

Mengubah indeks dalam urutan selektivitas tidak memengaruhi jumlah perbandingan kunci yang diharapkan dari pencarian biner atau jumlah halaman yang perlu dinavigasi untuk melakukan pencarian indeks. Paling-paling itu mungkin sedikit mempercepat perbandingan kunci itu sendiri.

Kadang-kadang memesan indeks yang paling selektif terlebih dahulu akan masuk akal untuk pertanyaan lain dalam beban kerja Anda.

Misalnya jika beban kerja berisi kueri dari kedua formulir berikut.

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

Indeks di atas tidak mencakup keduanya. MostSelectivecukup selektif untuk membuat rencana dengan pencarian dan pencarian bermanfaat tetapi permintaan terhadap Leasttidak.

Namun skenario ini (pencarian non-cakupan indeks pada subset kolom utama dari indeks komposit) hanya satu kelas query yang mungkin dapat dibantu oleh indeks. Jika Anda tidak pernah benar-benar mencari MostSelectivesendiri atau kombinasi dari MostSelective, SecondMostdan selalu mencari dengan kombinasi ketiga kolom maka keuntungan teoretis ini tidak berguna bagi Anda.

Sebaliknya, pertanyaan seperti

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

Akan terbantu dengan memiliki urutan terbalik dari yang biasa ditentukan - karena mencakup permintaan, dapat mendukung pencarian dan mengembalikan baris dalam urutan yang diinginkan untuk boot.

Jadi ini adalah saran yang sering diulang, tetapi paling tidak heuristik tentang manfaat potensial untuk pertanyaan lain - dan itu bukan pengganti untuk benar-benar melihat beban kerja Anda .

Martin Smith
sumber
31

Anda harus meletakkan kolom yang paling selektif di awal deklarasi indeks.

Benar. Indeks dapat berupa komposit - terdiri dari banyak kolom - dan urutannya penting karena prinsip paling kiri. Alasannya adalah, bahwa database memeriksa daftar dari kiri ke kanan, dan harus menemukan referensi kolom yang sesuai dengan urutan yang ditentukan. Misalnya, memiliki indeks pada tabel alamat dengan kolom:

  • Alamat
  • Kota
  • Negara

Setiap kueri yang menggunakan addresskolom dapat memanfaatkan indeks, tetapi jika kueri hanya memiliki salah satu citydan / atau statereferensi - indeks tidak dapat digunakan. Ini karena kolom paling kiri tidak dirujuk. Kinerja kueri akan memberi tahu Anda mana yang optimal - indeks individual, atau beberapa komposit dengan pesanan berbeda. Bacaan baik: The Tipping Point , oleh Kimberley Tripp

OMG Ponies
sumber
Bagaimana jika itu hanya kolom paling kanan yang tidak digunakan? Jadi permintaan menggunakan Alamat dan kota, tetapi TIDAK menyatakan. Apakah indeks akan digunakan?
Abe Miessler
@ Abe: Paling kanan tidak akan digunakan - Anda harus memenuhi urutan indeks mulai dari kiri. Nona satu, tidak bisa menggunakannya.
OMG Ponies
4
@Abe: Jika Anda menanyakan Alamat dan kota, tetapi TIDAK menyatakan - maka ya, indeks akan digunakan. Dengan kata lain, database dapat menggunakan indeks parsial untuk memenuhi permintaan, asalkan itu bisa mulai dari kiri indeks dan bergerak ke kanan dalam menggunakan bidang yang sedang ditanyakan. Namun, jika Anda ditanya menggunakan Alamat dan Negara, tetapi BUKAN kota, itu mungkin masih menggunakan indeks, tetapi itu tidak akan seefisien - karena sekarang hanya dapat menggunakan bagian Alamat dari indeks (b / c selanjutnya adalah kota dan tidak digunakan dalam kueri).
JaredC
6

Semua jawaban lainnya salah.

Selektivitas kolom individu dalam indeks komposit tidak masalah ketika memilih pesanan.

Ini adalah proses berpikir sederhana: Secara efektif, indeks adalah gabungan kolom-kolom yang terlibat.

Memberikan alasan itu, satu-satunya perbedaan adalah membandingkan dua 'string' yang berbeda sebelumnya dibandingkan kemudian dalam string. Ini adalah bagian kecil dari total biaya. Tidak ada "pass pertama / pass kedua", sebagaimana disebutkan dalam satu Jawaban.

Jadi, pesanan apa yang harus digunakan?

  1. Mulailah dengan kolom yang diuji dengan =, dalam urutan apa pun .
  2. Kemudian tempel pada satu kolom rentang.

Misalnya, kolom selektivitas yang sangat rendah harus didahulukan dalam hal ini:

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

Menukar urutan dalam indeks akan membuatnya benar-benar diabaikan deleted.

(Ada lebih banyak aturan untuk memesan kolom.)

Rick James
sumber
Apakah suara negatif karena saya salah? Atau karena saya punya pendapat yang kuat? Atau sesuatu yang lain?
Rick James
bukan downvote saya, tetapi dihapus = 0 bagi saya terdengar seperti itu bukan selektivitas rendah? Saya membayangkan itu akan menjadi mayoritas baris dalam tabel.
Greg
@Reg - Saya pikir itu berarti "selektivitas rendah" - Artinya, menggunakan deletedtidak banyak membantu dalam menyaring baris yang tidak diinginkan. Apakah Anda memiliki contoh yang lebih baik? (Itulah yang muncul di benak saya ketika saya menulis Jawaban.)
Rick James
Kesalahpahaman di pihak saya.
Greg
1
@ClickOk - Terima kasih. Buku masak saya memberikan beberapa info dasar: mysql.rjweb.org/doc.php/index_cookbook_mysql
Rick James