Mengapa pengoptimal memilih Indeks Clustered + Sortir bukan Indeks Non-Clustered?


11

Diberikan contoh berikut:

IF OBJECT_ID('dbo.my_table') IS NOT NULL
    DROP TABLE [dbo].[my_table];
GO

CREATE TABLE [dbo].[my_table]
(
    [id]    int IDENTITY (1,1)  NOT NULL PRIMARY KEY,
    [foo]   int                 NULL,
    [bar]   int                 NULL,
    [nki]   int                 NOT NULL
);
GO

/* Insert some random data */
INSERT INTO [dbo].[my_table] (foo, bar, nki)
SELECT TOP (100000)
    ABS(CHECKSUM(NewId())) % 14,
    ABS(CHECKSUM(NewId())) % 20,
    n = CONVERT(INT, ROW_NUMBER() OVER (ORDER BY s1.[object_id]))
FROM 
    sys.all_objects AS s1 
CROSS JOIN 
    sys.all_objects AS s2
GO

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC);
GO

Jika saya mengambil semua catatan yang dipesan oleh [nki](Non-clustered index):

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 266 ms, elapsed time = 493 ms

Pengoptimal memilih indeks berkerumun dan kemudian menerapkan algoritma Urut.

masukkan deskripsi gambar di sini

Execution plan

Tetapi jika saya memaksanya untuk menggunakan indeks non-cluster:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 311 ms, elapsed time = 188 ms

Kemudian ia menggunakan indeks non-clustered dengan Pencarian Kunci:

masukkan deskripsi gambar di sini

Execution plan

Jelas jika indeks yang tidak berkerumun diubah menjadi indeks yang meliputi:

CREATE UNIQUE NONCLUSTERED INDEX [IX_my_table]
    ON [dbo].[my_table] ([nki] ASC)
    INCLUDE (id, foo, bar);
GO

Maka hanya menggunakan indeks ini:

SET STATISTICS TIME ON;
SELECT id, foo, bar, nki FROM my_table ORDER BY nki;
SET STATISTICS TIME OFF;

SQL Server Execution Times: CPU time = 32 ms, elapsed time = 106 ms

masukkan deskripsi gambar di sini

Execution plan


Pertanyaan

  • Mengapa SQL Server menggunakan indeks berkerumun ditambah algoritma pengurutan alih-alih menggunakan indeks non-berkerumun bahkan jika waktu eksekusi 38% lebih cepat dalam kasus terakhir?

1
Apakah Anda bermaksud meninggalkan ORDER OLEH dalam permintaan indeks paksa Anda?
Forrest

Jawaban:


9

Mengapa SQL Server menggunakan indeks berkerumun ditambah algoritma pengurutan alih-alih menggunakan indeks non-berkerumun bahkan jika waktu eksekusi 38% lebih cepat dalam kasus terakhir?

Karena SQL Server menggunakan pengoptimal berbasis biaya berdasarkan statistik, bukan info runtime.

Selama proses estimasi biaya untuk kueri ini, ia sebenarnya mengevaluasi rencana pencarian, tetapi memperkirakan itu akan membutuhkan lebih banyak usaha. (Catat "Estimasi Biaya Subtree" ketika melayang di atas SELECT dalam rencana eksekusi). Itu belum tentu asumsi yang buruk juga - pada mesin uji saya, rencana pencarian mengambil 6X CPU dari jenis / pemindaian.

Lihatlah ke jawaban Rob Farley tentang mengapa SQL Server mungkin membuat rencana pencarian lebih mahal.


9

Jika Anda membandingkan jumlah bacaan yang diperlukan dalam 100.000 pencarian dengan apa yang terlibat dalam melakukan pengurutan, Anda mungkin dengan cepat mendapatkan ide tentang mengapa Pengoptimal Kueri memperkirakan bahwa CIX + Sort akan menjadi pilihan terbaik.

Eksekusi Pencarian akhirnya menjadi lebih cepat karena halaman yang sedang dibaca berada dalam memori (bahkan jika Anda menghapus cache, Anda memiliki banyak baris per halaman, sehingga Anda membaca halaman yang sama berulang-ulang, tetapi dengan jumlah fragmentasi yang berbeda atau tekanan memori yang berbeda dari aktivitas lain, ini mungkin tidak terjadi). Ini benar-benar tidak akan mengambil banyak untuk memiliki CIX + Sort lebih cepat, tetapi apa yang Anda lihat adalah karena biaya membaca tidak mempertimbangkan relatif murahnya memukul halaman yang sama berulang kali.


4

Saya telah memutuskan untuk menggali sedikit pada pertanyaan ini dan saya menemukan beberapa dokumen menarik berbicara tentang bagaimana dan kapan menggunakan atau mungkin lebih baik, bukan (memaksa) penggunaan indeks non-cluster.

Seperti yang disarankan per komentar oleh John Eisbrener , salah satu yang paling direferensikan, bahkan di blog lain, adalah artikel yang menarik dari Kimberly L. Tripp:

tetapi ini bukan satu-satunya, jika Anda tertarik, Anda dapat melihat halaman ini:

Seperti yang Anda lihat, semuanya bergerak di sekitar konsep titik kritis .

Dikutip dari artikel KL Tripp

Apa titik kritisnya?

Ini adalah titik di mana jumlah baris yang dikembalikan adalah " tidak lagi cukup selektif ". SQL Server memilih untuk TIDAK menggunakan indeks non-cluster untuk mencari baris data yang sesuai dan melakukan pemindaian tabel.

Ketika SQL Server menggunakan indeks non-clustered pada heap, pada dasarnya ia mendapatkan daftar pointer ke halaman-halaman tabel dasar. Kemudian menggunakan pointer ini untuk mengambil baris dengan serangkaian operasi yang disebut Row ID Lookups (RID). Ini berarti bahwa setidaknya, ia akan menggunakan halaman yang dibaca sebanyak jumlah baris yang dikembalikan, dan mungkin lebih banyak lagi. Prosesnya agak mirip dengan indeks berkerumun sebagai tabel dasar, dengan hasil yang sama: lebih banyak dibaca.

Tapi, kapan titik kritis itu terjadi?

Tentu saja karena kebanyakan hal dalam hidup ini, itu tergantung ...

Tidak serius, ini terjadi antara 25% dan 33% dari jumlah halaman dalam tabel, tergantung pada berapa banyak baris per halaman. Tetapi ada lebih banyak faktor yang harus Anda pertimbangkan:

Dikutip dari artikel ITPRoToday

Faktor-Faktor Lain yang Mempengaruhi Tipping Point Meskipun biaya pencarian RID adalah faktor paling penting yang mempengaruhi titik kritis, ada sejumlah faktor lain:

  • I / O Fisik jauh lebih efisien saat memindai indeks berkerumun. Data indeks yang dikelompokkan ditempatkan secara berurutan pada disk dalam urutan indeks. Akibatnya, ada sedikit perjalanan lateral head pada disk, yang meningkatkan kinerja I / O.
  • Ketika mesin basis data memindai indeks berkerumun, ia tahu bahwa ada kemungkinan besar bahwa beberapa halaman berikutnya pada trek disk masih akan berisi data yang dibutuhkan. Jadi, itu mulai membaca di 64KB potongan daripada halaman 8KB normal. Ini juga menghasilkan I / O yang lebih cepat.

Sekarang jika saya menjalankan pertanyaan saya lagi menggunakan statistik IO:

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WHERE nki < 20000 ORDER BY nki ;
SET STATISTICS IO OFF;

Logical reads: 312

SET STATISTICS IO ON;
SELECT id, foo, bar, nki FROM my_table WITH(INDEX(IX_my_TABLE));
SET STATISTICS IO OFF;

Logical reads: 41293

Kueri kedua membutuhkan lebih banyak bacaan logis daripada yang pertama.

Haruskah saya menghindari indeks non-cluster?

Tidak, indeks berkerumun dapat berguna, tetapi layak untuk mengambil waktu dan melakukan upaya ekstra menganalisis apa yang ingin Anda capai dengannya.

Dikutip dari artikel KL Tripp

Jadi, apa yang harus kamu lakukan? Tergantung. Jika Anda tahu data Anda dengan baik dan Anda melakukan beberapa pengujian ekstensif Anda mungkin mempertimbangkan untuk menggunakan petunjuk (ada beberapa hal pintar yang dapat Anda lakukan secara terprogram dalam sps, saya akan mencoba dan mendedikasikan posting untuk ini segera). Namun, pilihan yang jauh lebih baik (jika memungkinkan) adalah mempertimbangkan untuk menutupi (itu benar-benar poin utama saya :). Dalam pertanyaan saya, sampul tidak realistis karena pertanyaan saya ingin semua kolom (SELECT jahat *) tetapi, jika pertanyaan Anda lebih sempit DAN mereka prioritas tinggi, Anda lebih baik dengan indeks penutup (dalam banyak kasus) melalui petunjuk karena indeks yang mencakup kueri, tidak pernah kiat.

Itulah jawaban untuk teka-teki untuk saat ini, tetapi pasti ada lebih banyak hal untuk diselami. Tipping Point bisa menjadi hal yang sangat bagus - dan biasanya bekerja dengan baik. Tetapi, jika Anda menemukan bahwa Anda dapat memaksakan indeks dan mendapatkan kinerja yang lebih baik, Anda mungkin ingin melakukan investigasi dan melihat apakah ini. Kemudian pertimbangkan seberapa besar kemungkinan sebuah petunjuk dapat membantu dan sekarang Anda tahu di mana Anda bisa fokus.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.