有些時候雖然有索引,可是不被優化器選擇使用,下面是開發過程當中遇到的不能使用索引的幾種狀況:mysql
1.以%開頭的like查詢不可以利用B-tree索引,執行計劃中key的值爲NULL表示沒有使用索引。sql
mysql> explain select * from actor where last_name like '%NI%'; +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | actor | NULL | ALL | NULL | NULL | NULL | NULL | 200 | 11.11 | Using where | +----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.42 sec)
數據庫中InnoDB引擎默認使用B-Tree索引,再B-Tree索引結構中,以%開頭的查詢天然就法利用索引了。數據庫
通常都推薦使用全文索引(Full-text)來解決相似的全文檢索問題。(這裏沒有給出全文索引的解決方案,能夠本身查一下)優化
除此以外你們還能夠利用InnoDB的表都是聚族表的特色,採起一種輕量級別的解決方式:code
通常狀況下,索引都會比表小,掃描索引比掃描表更快(特殊狀況下,索引會比表更大)。索引
在InnoDB表上二級索引idx_last_name實際上存儲字段last_name和主鍵actor_id,那麼理想的訪問方式應該是先掃描二級索引idx_last_name得到知足條件 last_name like '%NI%'的主鍵actor_id列表,以後根據主鍵回表去檢索記錄,這樣就避免了全表掃描演員表actor產生的大量IO請求。ip
mysql> explain select * from (select actor_id from actor where last_name like '%NI%') a,actor b where a.actor_id = b.actor_id; +----+-------------+-------+------------+--------+---------------+---------------------+---------+-----------------------+------+----------+--------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+--------+---------------+---------------------+---------+-----------------------+------+----------+--------------------------+ | 1 | SIMPLE | actor | NULL | index | PRIMARY | idx_actor_last_name | 137 | NULL | 200 | 11.11 | Using where; Using index | | 1 | SIMPLE | b | NULL | eq_ref | PRIMARY | PRIMARY | 2 | sakila.actor.actor_id | 1 | 100.00 | NULL | +----+-------------+-------+------------+--------+---------------+---------------------+---------+-----------------------+------+----------+--------------------------+ 2 rows in set, 1 warning (0.00 sec)
經過執行計劃中能夠看到,內層查詢的Using index 表明索引覆蓋掃描,以後經過主鍵join操做去演員表actor中獲取最終查詢結果,理論上比直接掃描全表掃描更快一些。 開發
2.數據類型出現隱式轉換的時候也不會使用索引字符串
特別是在當列類型是字符串時,那麼必定記得在where條件中把字符常量的值用引號引發來,不然即使這個列上有索引,MYSQL也不會使用,由於MYSQL默認把常量值進行轉換後才進行檢索。it
例如:演員表actor中姓氏字段last_name是字符型的,可是SQL語句的條件值1是一個數值類型,所以即使存在索引idx_last_name,MYSQL也不能正確使用索引,而是進行全表掃描。
mysql> explain select * from actor where last_name =1; +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | actor | NULL | ALL | idx_actor_last_name | NULL | NULL | NULL | 200 | 10.00 | Using where | +----+-------------+-------+------------+------+---------------------+------+---------+------+------+----------+-------------+ 1 row in set, 3 warnings (0.08 sec)
加上引號以後,再執行一次,就發先使用上索引了。
mysql> explain select * from actor where last_name ='1'; +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+ | 1 | SIMPLE | actor | NULL | ref | idx_actor_last_name | idx_actor_last_name | 137 | const | 1 | 100.00 | NULL | +----+-------------+-------+------------+------+---------------------+---------------------+---------+-------+------+----------+-------+ 1 row in set, 1 warning (0.10 sec)
3.複合索引查詢條件不符合最左匹配原則,是不會使用複合索引的。
最左匹配原則是要知足複合索引最左邊的字段,若是查詢條件不是第一個複合索引的第一個字段,則不符合最左匹配原則。
mysql> explain select * from payment where amount =3.98 and last_update ='2006-02-15 22:12:32'; +----+-------------+---------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+---------------+------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | payment | NULL | ALL | NULL | NULL | NULL | NULL | 16086 | 1.00 | Using where | +----+-------------+---------+------------+------+---------------+------+---------+------+-------+----------+-------------+ 1 row in set, 1 warning (0.04 sec)
4.若是MySQL估計使用索引比全表掃描慢,則不使用索引。
例如:查詢以「S」開頭的標題的電影,須要返回的記錄比例較大,MySQL就預估索引掃描還不如全表掃描更快:
mysql> update film_text set title =concat('s',title); Query OK, 1000 rows affected (3.47 sec) Rows matched: 1000 Changed: 1000 Warnings: 0 mysql> explain select * from film_text where title like 's%'; +----+-------------+-----------+------------+------+-----------------------+------+---------+------+------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+------+----------+-------------+ | 1 | SIMPLE | film_text | NULL | ALL | idx_title_description | NULL | NULL | NULL | 1000 | 11.11 | Using where | +----+-------------+-----------+------------+------+-----------------------+------+---------+------+------+----------+-------------+ 1 row in set, 1 warning (0.00 sec) 在MySQL 5.6版本中,可以經過Trace 清晰地看到優化器的選擇過程,對用時少的進行選擇。 **5.用or分割開的條件,若是or前的條件中有列索引,然後面的列中沒有索引,那麼涉及的索引都不會被用到。** mysql> explain select * from payment where customer_id =203 or amount =3.96; +----+-------------+---------+------------+------+--------------------+------+---------+------+-------+----------+-------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+------+--------------------+------+---------+------+-------+----------+-------------+ | 1 | SIMPLE | payment | NULL | ALL | idx_fk_customer_id | NULL | NULL | NULL | 16086 | 10.15 | Using where | +----+-------------+---------+------------+------+--------------------+------+---------+------+-------+----------+-------------+ 1 row in set, 1 warning (0.00 sec)
由於or後面的條件列中沒有索引,那麼後面的查詢確定要走全表掃描,在存在全表掃描的狀況下,就沒有必要多作一次索引掃描增長I/O訪問,一次全表掃過濾條件就足夠了。
當or先後兩個條件都有索引時,是會用到索引的。
mysql> explain select * from payment where customer_id= 203 or amount =3.96; +----+-------------+---------+------------+-------------+-------------------------------+-------------------------------+---------+------+------+----------+---------------------------------------------------------+ | id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra | +----+-------------+---------+------------+-------------+-------------------------------+-------------------------------+---------+------+------+----------+---------------------------------------------------------+ | 1 | SIMPLE | payment | NULL | index_merge | idx_fk_customer_id,idx_amount | idx_fk_customer_id,idx_amount | 2,3 | NULL | 21 | 100.00 | Using union(idx_fk_customer_id,idx_amount); Using where | +----+-------------+---------+------------+-------------+-------------------------------+-------------------------------+---------+------+------+----------+---------------------------------------------------------+ 1 row in set, 1 warning (0.05 sec)
索引的總結:若是在查詢條件中,有字段進行全表掃描,那麼索引也就不會被使用。條件中使用複合索引時,要遵照複合索引的使用規則,否則索引也不會被使用。