Indeks penutup digunakan meskipun kolom tidak ada

8

Saya memiliki pertanyaan berikut, menggunakan MariaDB 10 / InnoDB:

SELECT id, sender_id, receiver_id, thread_id, date_created, content 
FROM user_message 
WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Kueri ini mengambil pesan sesuai dengan kondisi dan jenis yang diberikan berdasarkan tanggal yang dibuat.

Saya memiliki indeks penutupan (thread_id, date_created).

Saat menjalankan EXPLAIN, indeks yang benar digunakan dan saya mendapatkan output "Menggunakan di mana", meskipun kueri menggunakan kolom di tengah pernyataan yang tidak ada dalam indeks. Saya dapat menggunakan nilai apa pun untuk "placeholder = x" dan hasilnya sama.

Jika saya mengubah penyortiran untuk menggunakan kolom lain, EXPLAIN dengan benar menunjukkan "Menggunakan di mana. Menggunakan filesort."

Saya mengalami momen menggaruk kepala. Adakah yang bisa menjelaskan hal ini? Apa yang saya harapkan untuk melihat adalah bahwa filesort tambahan akan diperlukan karena indeks penutup tidak dapat sepenuhnya digunakan karena kolom tambahan.

Tom
sumber

Jawaban:

8


Kueri Kasus A :

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY some_column DESC 
LIMIT 20

Indeks:

(thread_id, date_created)

Rencana:

Index is used
Using Where
Using filesort

Tidak masalah di sana, kan? Jika indeks digunakan (untuk mencocokkan sebagian WHEREkondisi), kami masih memerlukan operasi pengurutan untuk memesan hasilnya dengan some_column(yang tidak ada dalam indeks). Kami juga membutuhkan pemeriksaan tambahan (Menggunakan Di Mana) untuk menjaga hanya baris yang cocok dengan kondisi ke-2 juga. BAIK.


Kasus B (pertanyaan)
Pertanyaan:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Indeks:

(thread_id, date_created)

Rencana:

Index is used
Using Where
-- no "Using filesort"

Jadi, mengapa tidak perlu disortir di sini ? Karena indeks sudah cukup untuk disortir sesuai permintaan kueri. Tentu saja ada masalah tambahan dari kondisi ekstra ( AND placeholder = FALSE) yang tidak tercakup oleh indeks.

OK tapi kita tidak benar-benar membutuhkan yang di sini. Indeks dapat memberikan kami hasil yang sesuai dengan kondisi pertama ( WHERE thread_id = 12345) dan berada dalam urutan yang diinginkan untuk hasil. Satu-satunya pemeriksaan tambahan yang kita butuhkan - dan apa yang dilakukan rencana - adalah untuk mendapatkan baris dari tabel, dalam urutan yang disediakan oleh indeks, dan memeriksa kondisi ke-2 ini sampai kita mendapatkan 20 pertandingan. Itulah arti ** Menggunakan Di mana "".

Kami mungkin mendapatkan 20 pertandingan di 20 baris pertama (sangat bagus dan cepat) atau di 100 pertama (masih mungkin cukup cepat) atau di 1000000 pertama (mungkin sangat, sangat lambat) atau kami mungkin hanya mendapatkan 19 pertandingan dari tabel bahkan setelah membaca semua baris yang cocok dari indeks (benar-benar sangat lambat di meja besar). Itu semua tergantung pada distribusi data.


Kasus C (bahkan rencana yang lebih baik)
Kueri:

WHERE thread_id = 12345 
  AND placeholder = FALSE
ORDER BY date_created DESC 
LIMIT 20

Indeks:

(placeholder, thread_id, date_created)

Rencana:

Index is used
-- no "Using Where"
-- no "Using filesort"

Sekarang indeks kami cocok dengan kondisi dan urutan. Rencananya cukup sederhana: dapatkan * 20 kecocokan pertama dari indeks dan baca baris yang sesuai dari tabel. Tidak diperlukan pemeriksaan tambahan (Tidak Ada "Menggunakan Tempat") dan tidak ada jenis (tidak "Menggunakan Filesort")

first *: 20 pertama saat membaca indeks mundur dari akhir (seperti yang kita miliki ORDER BY .. DESC) tapi itu tidak masalah. Indeks B-tree dapat dibaca maju dan mundur dengan kinerja yang hampir sama.

ypercubeᵀᴹ
sumber
7
  • Menggunakan indeks mengindikasikan sebuah " Menutupi index" - Semua kolom di mana saja di SELECTberada di mana saja di indeks satu. Jadi, Anda tidak memiliki indeks "mencakup". Dan tidak praktis untuk membuat indeks penutup untuk permintaan Anda (kolom terlalu banyak disebutkan).
  • Menggunakan tempat - sebagian besar suara.
  • Menggunakan filesort - Kueri perlu diurutkan, tetapi mungkin dalam RAM atau di tabel temp. Dan mungkin ada beberapa macam (misalnya, GROUP BY x ORDER BY b)
  • Salah satu dari ini akan memungkinkan untuk hanya melihat 20 baris; indeks lainnya akan membutuhkan lebih banyak baris disentuh, mungkin seluruh tabel:

    INDEX(thread_id, placeholder, date_created)
    INDEX(placeholder, thread_id, date_created)
  • Tidak, kardinalitas komponen indeks komposit tidak masalah ketika memesan kolom dalam indeks.

My Cookbook menjelaskan cara menurunkan indeks optimal, mengingat a SELECT.

Rick James
sumber
Terima kasih untuk buku masak - lembaran yang sangat bagus.
Tom