Pencarian Indeks vs Pemindaian Indeks

64

Melihat rencana eksekusi permintaan yang berjalan lambat dan saya perhatikan bahwa beberapa node adalah pencarian indeks dan beberapa dari mereka adalah scan indeks.

Apa perbedaan antara pencarian indeks dan pemindaian indeks?

Yang berkinerja lebih baik?

Bagaimana SQL memilih satu dari yang lain?

Saya menyadari ini adalah 3 pertanyaan tetapi saya pikir menjawab yang pertama akan menjelaskan yang lain.

Greg
sumber
6
Anda memiliki referensi yang bagus tentang penggunaan-indeks-Lukas .
Marian
7
Tidak semua pemindaian buruk - terkadang ini adalah cara paling efisien untuk memenuhi kueri. Perhatikan juga bahwa tidak semua pencarian adalah pencarian - seringkali mereka sebenarnya adalah pemindaian jangkauan, dan pencarian hanya menunjukkan bagaimana pencarian tersebut dimulai dari rentang tersebut.
Aaron Bertrand
@ AaronBertrand tetapi jika sudah sampai ke awal rentang dan membacanya, itu pada dasarnya berarti Anda memerlukan data pula. Juga, mencari akhir kisaran.
George Polevoy

Jawaban:

76

Versi singkat: seek jauh lebih baik

Versi yang lebih singkat: seek umumnya jauh lebih baik, tetapi banyak sekali pencarian (yang disebabkan oleh desain kueri yang buruk dengan sub-kueri berkorelasi buruk misalnya, atau karena Anda membuat banyak kueri dalam operasi kursor atau loop lain) bisa lebih buruk daripada memindai, terutama jika kueri Anda mungkin mengembalikan data dari sebagian besar baris di tabel yang terpengaruh.

Ini membantu untuk mencakup seluruh keluarga untuk operasi pencarian data untuk sepenuhnya memahami implikasi kinerja.

Pemindaian Tabel: Tanpa indeks sama sekali yang relevan dengan permintaan Anda, perencana dipaksa untuk menggunakan pemindaian tabel yang berarti bahwa setiap baris dilihat. Ini dapat menghasilkan setiap halaman yang berkaitan dengan data tabel dibaca dari disk yang sering merupakan kasus terburuk. Perhatikan bahwa untuk beberapa kueri itu akan menggunakan pemindaian tabel bahkan ketika indeks yang berguna hadir - ini biasanya karena data dalam tabel sangat kecil sehingga lebih sulit untuk menelusuri indeks (jika ini adalah kasus Anda akan mengharapkan berencana untuk berubah ketika data tumbuh, dengan asumsi ukuran selektivitas indeks baik).

Pemindaian Indeks dengan Pencarian Baris: Tanpa indeks yang dapat langsung digunakan untuk pencarian ditemukan, tetapi indeks yang berisi kolom yang tepat ada, pemindaian indeks dapat digunakan. Misalnya, jika Anda memiliki tabel besar dengan 20 kolom dengan indeks pada kolom1, col2, col3 dan Anda mengeluarkan SELECT col4 FROM exampletable WHERE col2=616, dalam hal ini pemindaian indeks untuk permintaan col2lebih baik daripada memindai seluruh tabel. Setelah baris yang cocok ditemukan maka halaman data harus dibaca ke col4 pickup untuk output (atau bergabung lebih lanjut) yang merupakan tahap "pencarian bookmark" ketika Anda melihatnya dalam rencana kueri.

Pemindaian Indeks tanpa Pencarian Baris: Jika contoh di atas adalah SELECT col1, col2, col3 FROM exampletable WHERE col2=616maka upaya ekstra untuk membaca halaman data tidak diperlukan: setelah pencocokan baris indeks col2=616ditemukan, semua data yang diminta diketahui. Inilah sebabnya mengapa Anda terkadang melihat kolom yang tidak akan pernah dicari, tetapi kemungkinan akan diminta untuk output, ditambahkan ke akhir indeks - ini dapat menyimpan pencarian baris. Saat menambahkan kolom ke indeks untuk alasan ini dan hanya alasan ini, tambahkan mereka dengan INCLUDEklausa untuk memberi tahu mesin bahwa tidak perlu mengoptimalkan tata letak indeks untuk permintaan berdasarkan kolom ini (ini dapat mempercepat pembaruan yang dibuat untuk kolom tersebut) . Pemindaian indeks dapat dihasilkan dari kueri tanpa klausa pemfilteran juga: SELECT col2 FROM exampletableakan memindai indeks contoh ini alih-alih halaman tabel.

Pencarian Indeks (dengan atau tanpa pencarian baris) : Dalam pencarian tidak semua indeks dipertimbangkan. Untuk kueri SELECT * FROM exampletable WHERE c1 BETWEEN 1234 AND 4567, mesin kueri dapat menemukan baris pertama yang akan cocok dengan melakukan pencarian berbasis pohon pada indeks, c1kemudian dapat menavigasi indeks secara berurutan hingga mencapai akhir rentang (ini sama dengan kueri karena c1=1234mungkin ada banyak baris yang cocok dengan kondisi bahkan untuk =operasi). Ini berarti bahwa hanya halaman indeks yang relevan (ditambah beberapa yang dibutuhkan untuk pencarian awal) yang perlu dibaca daripada setiap halaman dalam indeks (atau tabel).

Indeks Berkelompok: Dengan indeks berkerumun data tabel disimpan dalam node daun dari indeks itu bukannya dalam struktur tumpukan yang terpisah. Ini berarti bahwa tidak akan pernah perlu ada pencarian baris tambahan setelah menemukan baris menggunakan indeks itu tidak peduli kolom apa yang diperlukan [kecuali Anda memiliki data di luar halaman seperti TEXTkolom atau VARCHAR(MAX)kolom yang berisi data panjang].

Anda hanya dapat memiliki satu indeks berkerumun untuk alasan ini [1] , indeks berkerumun adalah tabel Anda alih-alih memiliki struktur tumpukan terpisah, jadi jika Anda menggunakan satu [2] pilih tempat Anda meletakkannya dengan hati-hati untuk mendapatkan keuntungan maksimal.

Perhatikan juga bahwa indeks berkerumun karena "kunci pengelompokan" untuk tabel dan termasuk dalam setiap indeks yang tidak berkerumun di atas tabel, sehingga indeks berkerumun lebar umumnya bukan ide yang baik.

[1] Sebenarnya, Anda dapat secara efektif memiliki beberapa indeks berkerumun dengan menetapkan indeks non-berkerumun yang mencakup atau menyertakan setiap kolom pada tabel, tetapi ini sepertinya akan membuang-buang ruang memiliki dampak kinerja penulisan jadi jika Anda mempertimbangkan untuk melakukannya pastikan kamu benar-benar perlu.

[2] Ketika saya mengatakan "jika Anda menggunakan clustered index", tidak diketahui bahwa itu umumnya direkomendasikan bahwa Anda lakukan memiliki satu di setiap meja. Ada pengecualian seperti pada semua aturan praktis, tabel yang melihat sedikit selain sisipan massal dan bacaan tidak berurutan (tabel pementasan untuk proses ETL mungkin) menjadi contoh counter paling umum.

Poin tambahan: Pemindaian Tidak Lengkap:

Penting untuk diingat bahwa tergantung pada sisa kueri pemindaian tabel / indeks mungkin tidak benar-benar memindai seluruh tabel - jika logika memungkinkan rencana kueri mungkin dapat menyebabkannya dibatalkan lebih awal. Contoh paling sederhana dari ini adalah SELECT TOP(1) * FROM HugeTable- jika Anda melihat rencana permintaan untuk itu Anda akan melihat bahwa hanya satu baris dikembalikan dari pemindaian dan jika Anda menonton statistik IO ( SET STATISTICS IO ON; SELECT TOP(1) * FROM HugeTable) Anda akan melihat bahwa itu hanya membaca angka yang sangat kecil halaman (mungkin hanya satu).

Hal yang sama dapat terjadi jika predikat a WHEREatau JOIN ... ONklausa dapat dijalankan bersamaan dengan pemindaian yang merupakan sumber jika datanya. Perencana kueri / pelari kadang-kadang bisa sangat pintar dalam mendorong predikat kembali ke sumber data untuk memungkinkan penghentian awal pemindaian dengan cara ini (dan kadang-kadang Anda bisa pandai mengatur ulang kueri untuk membantunya melakukannya!). Sementara data mengalir dari kanan ke kiri sesuai panah pada tampilan rencana kueri standar, logika berjalan dari kiri ke kanan dan setiap langkah (kanan-ke-kiri) tidak perlu dijalankan hingga selesai sebelum langkah berikutnya dapat dimulai. Dalam contoh sederhana di atas jika Anda melihat setiap blok dalam rencana kueri sebagai agen, SELECTagen meminta TOPagen untuk baris yang pada gilirannya memintaTABLE SCANagen untuk satu, kemudian SELECTagen meminta yang lain tetapi TOPagen tahu tidak perlu repot-repot untuk bahkan bertanya kepada pembaca tabel, SELECTagen mendapat tanggapan "tidak lagi relevan" dan tahu semua pekerjaan dilakukan. Banyak operasi memblokir optimasi semacam ini tentu saja sering kali dalam contoh yang lebih rumit pemindaian tabel / indeks benar - benar membaca setiap baris, tetapi berhati-hatilah untuk tidak sampai pada kesimpulan bahwa pemindaian apa pun pasti merupakan operasi yang mahal.

David Spillett
sumber
6

Secara umum, usahanya baik, pemindaian buruk.

Mencari di mana kueri dapat menggunakan indeks secara efektif, dan menggunakannya untuk menemukan baris yang dibutuhkannya.

Pemindaian adalah di mana kueri mencari melalui seluruh indeks mencoba menemukan apa yang dibutuhkannya.

Bagaimana cara SQL memilih? Jauh di dalam internal optimizer kueri, keputusan dibuat berdasarkan kueri Anda dan indeks yang tersedia dan informasi statistik yang terkait dengan indeks tersebut.

Ada beberapa buku untuk dibaca yang mungkin menarik di sini - Keduanya dari toko buku Gerbang Merah di http://www.red-gate.com/community/books/

  • Paket Eksekusi SQL Server oleh Grant Fritchey
  • Di dalam Pengoptimal Kueri oleh Benjamin Nevarez
  • Statistik SQL Server oleh Holger Schmeling
Thomas Rushton
sumber
7
Untuk rencana yang sama pemindaian tabel tunggal baik, satu juta pencarian buruk. Jadi pernyataan pertama Anda tidak sepenuhnya benar.
Marian
Memang, pencarian indeks dan pemindaian indeks masing-masing memiliki penggunaannya sendiri, Anda tidak bisa mengatakan satu lebih baik daripada yang lain TANPA konteks tabel dan kueri yang mendasarinya. Sebagian besar waktu, jika sebuah tabel memiliki statistik yang tidak akurat, rencana eksekusi dapat keluar sebagai kurang optimal, seperti pencarian indeks yang keliru dipilih daripada pemindaian indeks dan sebaliknya.
jyao
5

Jika Anda ingin menggali subjek, buku yang sangat membantu (setidaknya bagi saya) adalah Rencana Eksekusi SQL Server oleh Grant Fritchey, tersedia secara bebas di RedGate di sini .

Jika Anda memiliki pertanyaan seperti

SELECT *
FROM myTable

SQL Server kemungkinan akan menggunakan pemindaian Indeks, karena harus melalui semua baris untuk menampilkan hasil yang diperlukan.

Di sisi lain,

SELECT *
FROM myTable
WHERE myID = 1

pasti akan menghasilkan pencarian Indeks. SQL Server akan menggunakan struktur B-tree dari indeks myID dan mengambil baris yang tepat akan jauh lebih cepat.

KookieMonster
sumber
Saya tidak tahu apakah saya setuju dengan "pasti" - bahkan jika indeks memiliki myID sebagai kolom utama, pencarian mungkin bukan jawaban yang optimal (tergantung pada banyak hal, seperti apakah itu unik - yang mungkin benar di tabel pelanggan tetapi tidak untuk customerID di tabel pesanan, berapa banyak kolom yang harus dicakup tetapi tidak ada dalam indeks, dll).
Aaron Bertrand
Saya tidak berpikir jawaban ini benar-benar mencakup pertanyaan yang diajukan.
Zero3
5

Yang lain telah mendefinisikan dengan cukup baik perbedaan antara mencari dan memindai. Dalam hal ini, permintaan Anda sendiri dan perencana pelaksanaan harus memberi Anda informasi yang Anda butuhkan untuk melihat nilai mana yang digunakan sebagai predikat (filter) untuk permintaan di setiap bagian. Biasanya ini merupakan praktik yang baik untuk selalu menambahkan indeks non-clustered pada kunci asing, dan tergantung pada kasus penggunaan dalam kode program, Anda mungkin ingin melihat ke dalam membuat indeks multi-kolom tambahan atau memasukkan indeks kolom juga. Dengan terminologi yang disajikan di sini, pencarian google akan memberikan hasil yang layak pada contoh masing-masing.

Tetapi sebagai contoh, katakan kode Anda meminta Kolom A dan Kolom B pada filter yang diberikan, tetapi Anda juga ingin mengembalikan nilai-nilai Kolom C dan Kolom E, Anda mungkin ingin membuat indeks pada Kolom A dan B dengan TERMASUK opsi yang berisi Kolom C dan E. Dengan cara itu pencarian indeks tunggal akan mengembalikan semua yang Anda butuhkan, karena tidak perlu melakukan pencarian untuk mengambil nilai-nilai lain (C dan E) pada baris yang sama.

Kahn
sumber