Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Bagaimana saya bisa menghindari pemindaian tabel penuh pada kueri mysql ini?

Berdasarkan EXPLAIN output dalam pertanyaan Anda, Anda sudah memiliki semua indeks kueri seharusnya akan menggunakan, yaitu:

CREATE INDEX idx_zip_from_distance
  ON zipcode_distances (zipcode_from, distance, zipcode_to);
CREATE INDEX idx_zipcode ON venues (zipcode, id);
CREATE INDEX idx_venue_id ON events (venue_id);

(Saya tidak yakin dari nama indeks Anda apakah idx_zip_from_distance benar-benar menyertakan zipcode_to kolom. Jika tidak, Anda harus menambahkannya untuk menjadikannya indeks penutup . Juga, saya telah menyertakan venues.id kolom di idx_zipcode untuk kelengkapan, tetapi, dengan asumsi itu adalah kunci utama untuk tabel dan bahwa Anda menggunakan InnoDB, itu akan disertakan secara otomatis.)

Namun, sepertinya MySQL memilih rencana kueri yang berbeda, dan mungkin kurang optimal, di mana ia memindai semua acara, menemukan tempat dan kode pos mereka, dan baru kemudian memfilter hasilnya berdasarkan jarak. Ini bisa menjadi rencana kueri yang optimal, jika kardinalitas tabel peristiwa cukup rendah, tetapi dari fakta bahwa Anda mengajukan pertanyaan ini, saya menganggapnya tidak demikian.

Salah satu alasan untuk rencana kueri suboptimal bisa fakta bahwa Anda memiliki terlalu banyak indeks yang membingungkan perencana. Misalnya, apakah Anda benar-benar membutuhkan ketiga indeks tersebut pada tabel kode pos, mengingat data yang disimpannya mungkin simetris? Secara pribadi, saya hanya menyarankan indeks yang saya jelaskan di atas, ditambah indeks unik (yang juga bisa menjadi kunci utama, jika Anda tidak memiliki indeks buatan) di (zipcode_to, zipcode_from) (sebaiknya dalam urutan itu, sehingga ada pertanyaan sesekali di zipcode_to=? dapat memanfaatkannya).

Namun, berdasarkan beberapa pengujian yang saya lakukan, saya menduga masalah utama mengapa MySQL memilih rencana kueri yang salah hanya disebabkan oleh kardinalitas relatif tabel Anda. Agaknya, zipcode_distances Anda yang sebenarnya meja besar , dan MySQL tidak cukup pintar untuk menyadari seberapa banyak kondisi di WHERE klausa benar-benar mempersempitnya.

Jika demikian, perbaikan terbaik dan paling sederhana adalah dengan memaksa MySQL untuk menggunakan indeks yang Anda inginkan :

select
    *
from
    zipcode_distances z 
    FORCE INDEX (idx_zip_from_distance)
inner join
    venues v    
    FORCE INDEX (idx_zipcode)
    on z.zipcode_to=v.zipcode
inner join
    events e
    FORCE INDEX (idx_venue_id)
    on v.id=e.venue_id
where
    z.zipcode_from='92108' and
    z.distance <= 5

Dengan kueri itu, Anda memang harus mendapatkan paket kueri yang diinginkan. (Anda memang membutuhkan FORCE INDEX di sini, karena hanya dengan USE INDEX perencana kueri masih bisa memutuskan untuk menggunakan pemindaian tabel alih-alih indeks yang disarankan, mengalahkan tujuannya. Saya mengalami ini saat pertama kali menguji ini.)

hal. Berikut demo di SQLize, keduanya dengan dan tanpa FORCE INDEX , menunjukkan masalahnya.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Konsep MySQL:sesi vs koneksi

  2. memilih susunan tabel untuk karakter universal

  3. Pilih Baris dengan Grup Nilai Kolom Maksimum dengan Kolom Lain

  4. Tidak dapat terhubung ke server mysql dengan MAMP atau dengan Server Komunitas

  5. Python - mysqlDB, hasil sqlite sebagai kamus