Saya menjalankan EXPLAIN
:
mysql> explain select last_name from employees order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Indeks di meja saya:
mysql> show index from employees;
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| employees | 0 | PRIMARY | 1 | subsidiary_id | A | 6 | NULL | NULL | | BTREE | | |
| employees | 0 | PRIMARY | 2 | employee_id | A | 10031 | NULL | NULL | | BTREE | | |
| employees | 1 | idx_last_name | 1 | last_name | A | 10031 | 700 | NULL | | BTREE | | |
| employees | 1 | date_of_birth | 1 | date_of_birth | A | 10031 | NULL | NULL | YES | BTREE | | |
| employees | 1 | date_of_birth | 2 | subsidiary_id | A | 10031 | NULL | NULL | | BTREE | | |
+-----------+------------+---------------+--------------+---------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
5 rows in set (0.02 sec)
Ada indeks pada last_name tetapi pengoptimal tidak menggunakannya.
Jadi saya lakukan:
mysql> explain select last_name from employees force index(idx_last_name) order by last_name;
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
| 1 | SIMPLE | employees | ALL | NULL | NULL | NULL | NULL | 10031 | Using filesort |
+----+-------------+-----------+------+---------------+------+---------+------+-------+----------------+
1 row in set (0.00 sec)
Tapi tetap saja indeksnya tidak digunakan! Apa yang saya lakukan salah di sini?
Apakah itu ada hubungannya dengan fakta bahwa indeks itu NON_UNIQUE
? BTW, last_name adalahVARCHAR(1000)
Pembaruan diminta oleh @Rando_MySQLDBA
mysql> SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
+---------------+
| DistinctCount |
+---------------+
| 10000 |
+---------------+
1 row in set (0.05 sec)
mysql> SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
+----------+
| COUNT(1) |
+----------+
| 0 |
+----------+
1 row in set (0.15 sec)
SELECT COUNT(DISTINCT last_name) DistinctCount FROM employees;
2)SELECT COUNT(1) FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A;
. Apa hasil dari setiap penghitungan?SELECT COUNT(1) FullTableCount FROM employees;
dan 2)SELECT * FROM (SELECT COUNT(1) Count500,last_name FROM employees GROUP BY last_name HAVING COUNT(1) > 500) A LIMIT 10;
.Jawaban:
MASALAH # 1
Lihatlah kueri
Saya tidak melihat klausa WHERE yang berarti, dan begitu pula Pengoptimal Permintaan MySQL. Tidak ada insentif untuk menggunakan indeks.
MASALAH # 2
Lihatlah kueri
Anda memberikannya indeks, tetapi Query Opitmizer mengambil alih. Saya telah melihat perilaku ini sebelumnya ( Bagaimana cara memaksa JOIN untuk menggunakan indeks spesifik di MySQL? )
Mengapa ini harus terjadi?
Tanpa
WHERE
klausa, Pengoptimal Kueri mengatakan yang berikut untuk dirinya sendiri:WHERE
klausa?Pengoptimal Kueri memilih jalur dengan resistensi paling rendah.
Anda akan mengalami sedikit kejutan, tetapi ini dia: Tahukah Anda bahwa Query Optimizer akan menangani MyISAM dengan sangat berbeda?
Anda mungkin mengatakan HUH ???? BAGAIMANA ????
MyISAM menyimpan data dalam
.MYD
file dan semua indeks dalam.MYI
file.Permintaan yang sama akan menghasilkan rencana EXPLAIN yang berbeda karena indeks tinggal di file yang berbeda dari data. Mengapa Inilah alasannya:
last_name
kolom) sudah dipesan di.MYI
last_name
dari indeksBagaimana bisa begitu yakin tentang ini? Saya telah menguji teori kerja ini tentang bagaimana menggunakan penyimpanan yang berbeda akan menghasilkan rencana EXPLAIN yang berbeda (kadang-kadang lebih baik): Haruskah indeks mencakup semua kolom yang dipilih agar dapat digunakan untuk ORDER OLEH?
sumber
Sebenarnya, masalahnya di sini adalah ini terlihat seperti indeks awalan. Saya tidak melihat definisi tabel dalam pertanyaan, tetapi
sub_part
= 700? Anda belum mengindeks seluruh kolom, sehingga indeks tidak dapat digunakan untuk menyortir dan juga tidak berguna sebagai indeks penutup. Itu hanya dapat digunakan untuk menemukan baris yang "mungkin" cocok denganWHERE
dan lapisan server (di atas mesin penyimpanan) harus lebih lanjut menyaring baris yang cocok. Apakah Anda benar-benar membutuhkan 1000 karakter untuk nama belakang?pembaruan untuk menggambarkan: Saya memiliki tabel uji tabel dengan litle lebih dari 500 baris di dalamnya, masing-masing dengan nama domain dari situs web dalam kolom
domain_name VARCHAR(254) NOT NULL
dan tidak ada indeks.Dengan kolom lengkap diindeks, kueri menggunakan indeks:
Jadi, sekarang, saya akan menjatuhkan indeks itu, dan hanya mengindeks 200 karakter pertama dari nama_domain.
Voila.
Perhatikan juga, bahwa indeks, pada 200 karakter, lebih panjang dari nilai terpanjang di kolom ...
... tapi itu tidak ada bedanya. Indeks yang dideklarasikan dengan panjang awalan hanya dapat digunakan untuk pencarian, bukan untuk penyortiran, dan bukan sebagai indeks penutup, karena tidak berisi nilai kolom penuh, menurut definisi.
Selain itu, kueri di atas dijalankan pada tabel InnoDB, tetapi menjalankannya pada tabel MyISAM menghasilkan hasil yang hampir identik. Satu- satunya perbedaan dalam hal ini adalah bahwa InnoDB dihitung
rows
sedikit mati (541) sementara MyISAM menunjukkan jumlah baris yang tepat (563) yang merupakan perilaku normal karena dua mesin penyimpanan menangani penyelaman indeks sangat berbeda.Saya masih akan menyatakan bahwa kolom last_name kemungkinan lebih besar dari yang dibutuhkan, tetapi masih mungkin untuk mengindeks seluruh kolom, jika Anda menggunakan InnoDB dan menjalankan MySQL 5.5 atau 5.6:
sumber
varchar(1000)
tapi ini di luar batas maksimum yang diizinkan untuk indeks yaitu ~ 750EXPLAIN SELECT ...
, sertaSHOW CREATE TABLE ...
danSELECT @@VERSION;
karena perubahan ke pengoptimal lintas versi mungkin relevan.Saya membuat jawaban tentang karena komentar tidak akan mendukung pemformatan dan RolandoMySQL DBA berbicara tentang gen_clust_index dan innodb. Dan ini sangat penting pada tabel berbasis innodb. Ini lebih jauh dari pengetahuan DBA normal karena Anda harus dapat menganalisis kode C ..
Anda harus SELALU SELALU membuat KUNCI UTAMA atau KUNCI UNIK jika Anda menggunakan Innodb. Jika Anda tidak innodb akan menggunakan itu sendiri ROW_ID yang dihasilkan yang bisa lebih merugikan Anda daripada kebaikan.
Saya akan mencoba menjelaskannya dengan mudah karena buktinya didasarkan pada kode C.
Masalah pertama
mutex_enter (& (dict_sys-> mutex));
Baris ini memastikan hanya satu utas yang dapat mengakses dict_sys-> mutex secara bersamaan. Bagaimana jika nilai sudah ditransmisikan ... ya utas harus menunggu sehingga Anda mendapatkan sesuatu seperti fitur acak yang bagus seperti penguncian utas atau jika Anda memiliki lebih banyak tabel tanpa KUNCI UTAMA atau KUNCI UNIK Anda akan memiliki fitur yang bagus dengan innodb ' table locking ' bukankah ini bukan alasan mengapa MyISAM digantikan oleh InnoDB karena dari fitur yang bagus itu disebut penguncian berbasis catatan / baris ..
Masalah kedua
(0 == (id% DICT_HDR_ROW_ID_WRITE_MARGIN))
modulo (%) perhitungan lambat tidak baik jika Anda memasukkan batch karena harus dihitung ulang setiap kali ..., dan karena DICT_HDR_ROW_ID_WRITE_MARGIN (nilai 256) adalah kekuatan dua ini dapat dibuat lebih cepat ..
(0 == (id & (DICT_HDR_ROW_ID_WRITE_MARGIN - 1))))
Catatan tambahan jika kompiler C dikonfigurasi untuk mengoptimalkan dan itu adalah pengoptimal yang baik, optimizer C akan memperbaiki kode "berat" ke versi yang lebih ringan
moto cerita selalu buat KUNCI UTAMA Anda sendiri atau pastikan Anda memiliki indeks UNIK ketika Anda membuat tabel dari awal
sumber
UNIQUE
sudah cukup - itu juga perlu menyertakan hanya kolom non-NULL untuk indeks unik untuk dipromosikan menjadi PK.INSERT
dihabiskan dalam fungsi ini. Saya kira tidak penting. Kontras upaya untuk menyekop kolom di sekitar, lakukan operasi BTree, termasuk blok-split sesekali, berbagai mutex pada buffer_pool, ganti-penyangga, dll.