Indeks MySQL - apa praktik terbaik?

208

Saya telah menggunakan indeks pada database MySQL saya untuk sementara waktu sekarang tetapi tidak pernah mempelajarinya dengan benar . Secara umum saya menempatkan indeks pada bidang apa pun yang akan saya cari atau pilih menggunakan WHEREklausa tetapi kadang-kadang tidak tampak begitu hitam dan putih.

Apa praktik terbaik untuk indeks MySQL?

Contoh situasi / dilema:

  • Jika sebuah tabel memiliki enam kolom dan semuanya dapat dicari, haruskah saya mengindeks semuanya atau tidak sama sekali?

  • Apa dampak negatif kinerja pengindeksan?

  • Jika saya memiliki kolom VARCHAR 2500 yang dapat dicari dari bagian situs saya, haruskah saya mengindeksnya?

Haroldo
sumber
5
Anda mungkin harus mengulang pertanyaannya. Pilihan Indeks adalah bagian penting untuk optimasi setiap model basis data. Dan menurut saya tidak ada hubungannya dengan php.
VGE

Jawaban:

242

Anda pasti harus meluangkan waktu membaca indeks, ada banyak yang ditulis tentang itu, dan penting untuk memahami apa yang terjadi.

Secara umum, suatu indeks memaksakan pemesanan pada baris-baris tabel.

Demi kesederhanaan, bayangkan sebuah tabel hanyalah file CSV besar. Setiap kali baris dimasukkan, itu dimasukkan di akhir . Jadi urutan "alami" dari tabel hanyalah urutan memasukkan baris.

Bayangkan Anda memiliki file CSV yang dimuat dalam aplikasi spreadsheet yang sangat sederhana. Yang dilakukan spreadsheet ini hanyalah menampilkan data, dan menomori baris dalam urutan yang berurutan.

Sekarang bayangkan Anda perlu menemukan semua baris yang memiliki nilai "M" di kolom ketiga. Mengingat apa yang Anda miliki, Anda hanya memiliki satu opsi. Anda memindai tabel memeriksa nilai kolom ketiga untuk setiap baris. Jika Anda memiliki banyak baris, metode ini ("pemindaian tabel") dapat memakan waktu lama!

Sekarang bayangkan bahwa selain tabel ini, Anda punya indeks. Indeks khusus ini adalah indeks nilai di kolom ketiga. Indeks mencantumkan semua nilai dari kolom ketiga, dalam urutan yang bermakna (katakanlah, menurut abjad) dan untuk masing-masingnya, memberikan daftar nomor baris di mana nilai itu muncul.

Sekarang Anda memiliki strategi yang baik untuk menemukan semua baris di mana nilai kolom ketiga adalah "M". Misalnya, Anda dapat melakukan pencarian biner ! Sedangkan pemindaian tabel mengharuskan Anda untuk mencari N baris (di mana N adalah jumlah baris), pencarian biner hanya mengharuskan Anda melihat entri indeks log-n, dalam kasus yang paling buruk. Wow, itu pasti jauh lebih mudah!

Tentu saja, jika Anda memiliki indeks ini, dan Anda menambahkan baris ke tabel (pada akhirnya, karena itulah cara kerja tabel konseptual kami), Anda perlu memperbarui indeks setiap waktu. Jadi Anda melakukan lebih banyak pekerjaan saat Anda sedang menulis baris baru, tetapi Anda menghemat banyak waktu ketika Anda mencari sesuatu.

Jadi, secara umum, pengindeksan membuat tradeoff antara efisiensi baca dan efisiensi menulis. Tanpa indeks, sisipan bisa sangat cepat - mesin database hanya menambahkan baris ke tabel. Saat Anda menambahkan indeks, mesin harus memperbarui setiap indeks saat melakukan memasukkan.

Di sisi lain, membaca menjadi jauh lebih cepat.

Semoga itu mencakup dua pertanyaan pertama Anda (seperti yang sudah dijawab orang lain - Anda perlu menemukan keseimbangan yang tepat).

Skenario ketiga Anda sedikit lebih rumit. Jika Anda menggunakan LIKE, mesin pengindeksan biasanya akan membantu kecepatan baca Anda hingga "%" pertama. Dengan kata lain, jika Anda MEMILIH kolom MANA SEPERTI 'foo% bar%', database akan menggunakan indeks untuk menemukan semua baris tempat kolom dimulai dengan "foo", dan kemudian perlu memindai rowset perantara tersebut untuk menemukan subset yang mengandung "bar". PILIH ... DI MANA kolom SEPERTI '% bar%' tidak dapat menggunakan indeks. Saya harap Anda bisa melihat alasannya.

Akhirnya, Anda harus mulai memikirkan indeks pada lebih dari satu kolom. Konsepnya sama, dan berperilaku mirip dengan hal-hal seperti - pada dasarnya, jika Anda memiliki indeks pada (a, b, c), mesin akan terus menggunakan indeks dari kiri ke kanan sebaik mungkin. Jadi pencarian pada kolom a mungkin menggunakan indeks (a, b, c), seperti halnya pada (a, b). Namun, mesin perlu melakukan pemindaian tabel penuh jika Anda mencari di mana WHERE b = 5 AND c = 1)

Semoga ini sedikit membantu, tetapi saya harus menegaskan bahwa sebaiknya Anda menghabiskan beberapa jam untuk mencari artikel bagus yang menjelaskan hal-hal ini secara mendalam. Ini juga merupakan ide yang baik untuk membaca dokumentasi server database khusus Anda. Cara indeks diimplementasikan dan digunakan oleh perencana kueri dapat sangat bervariasi.

timdev
sumber
10
Bagaimana dengan FULLTEXTindeks? Bisakah mereka membantu dengan kondisi seperti LIKE '%bar%'?
Septagram
2
@Septagram - FULLTEXTdapat membantu dengan itu permintaan jika bar adalah "kata". FULLTEXTmenangani kata-kata, bukan substring sembarang (seperti LIKEhalnya).
Rick James
@timdev secara eksplisit di bagian mana dijawab pertanyaan pertama? Saya dapat mendeteksi pertanyaan kedua dan ketiga yang dijawab di bagian pertama dan kedua (sebelum dan sesudah dari Hopefully yang mencakup dua pertanyaan pertama Anda ) dari jawaban Anda yang berharga
Manuel Jordan
1
@ManuelJordan - Tidak ada jawaban sederhana untuk pertanyaan pertama. Itu tergantung pada bagaimana Anda ingin menyeimbangkan pertukaran dalam konteks penggunaan yang diantisipasi (atau bahkan lebih baik, diamati).
timdev
57

Lihat presentasi seperti More Mastering the Art of Indexing .

Pembaruan 12/2012: Saya telah memposting presentasi baru saya: Cara Mendesain Indeks, Sungguh . Saya mempresentasikan ini pada Oktober 2012 di ZendCon di Santa Clara, dan pada Desember 2012 di Percona Live London.

Merancang indeks terbaik adalah proses yang harus cocok dengan kueri yang Anda jalankan di aplikasi Anda.

Sulit untuk merekomendasikan aturan tujuan umum tentang kolom mana yang terbaik untuk diindeks, atau apakah Anda harus mengindeks semua kolom, tidak ada kolom, indeks mana yang harus menjangkau beberapa kolom, dll. Itu tergantung pada kueri yang perlu Anda jalankan.

Ya, ada beberapa overhead sehingga Anda tidak perlu membuat indeks dengan sia-sia. Tetapi Anda harus membuat indeks yang memberi manfaat pada kueri yang harus Anda jalankan dengan cepat. Overhead indeks biasanya jauh melebihi manfaatnya.

Untuk kolom yang VARCHAR (2500), Anda mungkin ingin menggunakan indeks FULLTEXT atau indeks awalan:

CREATE INDEX i ON SomeTable(longVarchar(100));

Perhatikan bahwa indeks konvensional tidak dapat membantu jika Anda mencari kata-kata yang mungkin berada di tengah-tengah varchar panjang itu. Untuk itu, gunakan indeks teks lengkap.

Bill Karwin
sumber
3
Terima kasih banyak. slideshare.net/matsunobu/… memang sangat membantu.
Bishal Paudel
1
Excellent the slideshare.net/billkarwin/how-to-design-indexes-really presentation
Manuel Jordan
1
Presentasi yang luar biasa (Yang dari 2012), benar-benar memahami seluruh poin indeks.
DarkteK
46

Saya tidak akan mengulangi beberapa saran bagus di jawaban lain, tetapi akan menambahkan:

Indeks Kompon

Anda dapat membuat indeks gabungan - indeks yang menyertakan banyak kolom. MySQL dapat menggunakannya dari kiri ke kanan . Jadi, jika Anda memiliki:

Table A
Id
Name
Category
Age
Description

jika Anda memiliki indeks gabungan yang menyertakan Nama / Kategori / Usia dalam urutan itu, klausa WHERE ini akan menggunakan indeks:

WHERE Name='Eric' and Category='A'

WHERE Name='Eric' and Category='A' and Age > 18

tapi

WHERE Category='A' and Age > 18

tidak akan menggunakan indeks itu karena semuanya harus digunakan dari kiri ke kanan.

Menjelaskan

Gunakan Jelaskan / Jelaskan Diperpanjang untuk memahami indeks apa yang tersedia untuk MySQL dan mana yang sebenarnya dipilih. MySQL hanya akan menggunakan SATU kunci per kueri .

EXPLAIN EXTENDED SELECT * from Table WHERE Something='ABC'

Log Permintaan Lambat

Aktifkan log kueri lambat untuk melihat kueri mana yang berjalan lambat.

Kolom Lebar

Jika Anda memiliki kolom lebar di mana PALING perbedaan terjadi pada beberapa karakter pertama, Anda hanya dapat menggunakan karakter N pertama dalam indeks Anda. Contoh: Kami memiliki kolom ReferenceNumber didefinisikan sebagai varchar (255) tetapi 97% dari kasus, nomor referensi adalah 10 karakter atau kurang. Saya mengubah indeks untuk hanya melihat 10 karakter pertama dan sedikit meningkatkan kinerja.

Eric J.
sumber
Saya punya pertanyaan tentang bagian terakhir. Saya membaca di suatu tempat bahwa jika Anda membuat kolom dengan VARCHAR Anda harus selalu mengaturnya menjadi 255. Sekarang Anda mengatakan bahwa indeks yang diatur untuk jenis kolom ini dapat dibatasi untuk hanya melihat 10 karakter pertama. Bagaimana tepatnya Anda bisa melakukan itu?
AlexioVay
20

Jika sebuah tabel memiliki enam kolom dan semuanya dapat dicari, haruskah saya mengindeks semuanya atau tidak sama sekali

Apakah Anda mencari pada bidang demi bidang atau beberapa pencarian menggunakan beberapa bidang? Bidang mana yang paling banyak dicari? Apa jenis bidangnya? (Indeks berfungsi lebih baik pada INT daripada pada VARCHAR misalnya) Sudahkah Anda mencoba menggunakan EXPLAIN pada kueri yang sedang dijalankan?

Apa dampak kinerja pengindeksan yang negatif

UPDATE dan INSERT akan lebih lambat. Ada juga persyaratan ruang penyimpanan tambahan, tetapi itu biasanya tidak penting akhir-akhir ini.

Jika saya memiliki kolom VARCHAR 2500 yang dapat dicari dari bagian situs saya, haruskah saya mengindeksnya

Tidak, kecuali itu UNIK (yang artinya sudah diindeks) atau Anda hanya mencari kecocokan persis pada bidang itu (tidak menggunakan LIKE atau pencarian teks lengkap mySQL).

Secara umum saya menempatkan indeks pada bidang apa pun yang akan saya cari atau pilih menggunakan klausa WHERE

Saya biasanya mengindeks bidang yang paling banyak ditanyakan, dan kemudian INTs / BOOLEANs / ENUM bukan bidang yang VARCHARS. Jangan lupa, seringkali Anda perlu membuat indeks pada bidang gabungan, bukan indeks pada bidang individual. Gunakan EXPLAIN, dan periksa log lambat.

Pete
sumber
11

Muat Data Secara Efisien : Indeks mempercepat pengambilan tetapi memperlambat penyisipan dan penghapusan, serta pembaruan nilai dalam kolom yang diindeks. Artinya, indeks memperlambat sebagian besar operasi yang melibatkan penulisan. Ini terjadi karena menulis satu baris memerlukan penulisan tidak hanya baris data, itu juga memerlukan perubahan pada indeks apa pun. Semakin banyak indeks yang dimiliki tabel, semakin banyak perubahan yang harus dilakukan, dan semakin besar penurunan kinerja rata-rata. Sebagian besar tabel menerima banyak pembacaan dan sedikit penulisan, tetapi untuk tabel dengan persentase penulisan yang tinggi, biaya pembaruan indeks mungkin signifikan.

Hindari Indeks : Jika Anda tidak memerlukan indeks tertentu untuk membantu kueri berkinerja lebih baik, jangan buat itu.

Ruang Disk : Indeks membutuhkan ruang disk, dan beberapa indeks juga memiliki lebih banyak ruang. Ini mungkin menyebabkan Anda mencapai batas ukuran tabel lebih cepat daripada jika tidak ada indeks. Hindari indeks sedapat mungkin.

Takeaway: Jangan over index

Srikar Doddi
sumber
5

Secara umum, indeks membantu mempercepat pencarian basis data, memiliki kelemahan menggunakan ruang disk tambahan dan memperlambat INSERT/ UPDATE/ DELETEquery. Gunakan EXPLAINdan baca hasilnya untuk mengetahui kapan MySQL menggunakan indeks Anda.

Jika sebuah tabel memiliki enam kolom dan semuanya dapat dicari, haruskah saya mengindeks semuanya atau tidak sama sekali?

Mengindeks semua enam kolom tidak selalu merupakan praktik terbaik.

(a) Apakah Anda akan menggunakan salah satu kolom tersebut ketika mencari informasi spesifik?

(B) Berapa selektivitas kolom-kolom itu (berapa banyak nilai berbeda yang disimpan, dibandingkan dengan jumlah total catatan pada tabel)?

MySQL menggunakan pengoptimal berbasis biaya, yang mencoba menemukan jalur "termurah" saat melakukan kueri. Dan bidang dengan selektivitas rendah bukan kandidat yang baik.

Apa dampak kinerja pengindeksan yang negatif?

Sudah dijawab: ruang disk tambahan, kinerja lebih rendah saat memasukkan - perbarui - hapus.

Jika saya memiliki kolom VARCHAR 2500 yang dapat dicari dari bagian situs saya, haruskah saya mengindeksnya?

Coba Indeks FULLTEXT .

Anax
sumber
4

1/2) Indeks mempercepat operasi tertentu tetapi memperlambat operasi lain seperti menyisipkan, memperbarui, dan menghapus. Ini bisa menjadi keseimbangan yang bagus.

3) gunakan indeks teks lengkap atau mungkin sphinx

Paul Creasey
sumber
Untuk mencegah slow down other operations like insert, update and deletesAnda dapat menggunakan START TRANSACTION; YOUR CODE HERE; COMMIT Yang dapat membantu menghindari slowing downoperasi lain, karena hanya akan memeriksa satu kendala satu kali. Peringatan: Jika Anda menggunakan REPLACE INTOdan Anda SQL_MODE<> STRICT_ALL_TABLESOR TRADITIONALThe Bulk Loadakan mengabaikan menggantikan ke dalam dan insert duplikat.
JayRizzo
Transaksi tidak didukung di semua mesin MySQL. AFAIK, transaksi memperlambat operasi DB, meskipun hanya digunakan secara implisit. Apa yang kita perlu desain berdasarkan kinerja aktual adalah beberapa cara semi-otomatis untuk membuat profil (mengukur kinerja) dari berbagai pilihan optimasi, termasuk indeks dan transaksi.
David Spector