MySQL數據SQL優化中,索引不被使用的典型場景總結

有些時候雖然有索引,可是不被優化器選擇使用,下面是開發過程當中遇到的不能使用索引的幾種狀況: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)

 索引的總結:若是在查詢條件中,有字段進行全表掃描,那麼索引也就不會被使用。條件中使用複合索引時,要遵照複合索引的使用規則,否則索引也不會被使用。

相關文章
相關標籤/搜索