sql優化篇(二):索引的使用

1、索引的存儲分類

  • B-tree索引:最常見的索引類型
  • HASH索引:只有Memory引擎支持
  • R-tree索引:空間索引MyISAM的一個特殊索引類型
  • Full-text索引:全文索引也是MyISAM,InnoDB從mysql5.6版本提供全文檢索的支持

B-tree索引和Hash索引比較:
Hash索引相對簡單,只有Memory引擎支持Hash索引,Hash索引適用於Key-value查詢,通過Hash索引要比通過B-tree索引查詢更迅速;Hash索引不適合範圍查詢,例如<、>、<=..這類。

2、Mysql如何適用索引

Mysql中能夠使用索引的典型場景

(1)匹配全值

對索引中具體的列指定具體的值:
繼續使用sakila作爲數據庫,重命名租賃表rental上的索引rental_date爲idx_rental_date;
這裏寫圖片描述
可以看到優化器選擇了複合索引idx_rental_date。

(2)匹配值的範圍查詢

這裏寫圖片描述

(3)匹配最左前綴(做左匹配原則可以算是MySQL中B-TREE索引使用的首要原則)

僅僅使用索引中的最左邊的列進行查找。

mysql>alter table payment add index idx_payment_date(payment_date,amount,last_update);

Query OK, 0 rows affected (0.14 sec)

Records: 0 Duplicates: 0  Warnings: 0

如果對第一個和第三個可以使用索引

mysql>explain select * from payment where payment_date='2006-02-14 15:16:03' andlast_update='2006-02-15 22:12:32'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ref

possible_keys: idx_payment_date

         key: idx_payment_date

     key_len: 5

         ref: const

         rows: 182

       Extra: Using index condition

1 row in set (0.00 sec)



ERROR:

No query specified

但是對第二個和第三個就不可以使用索引

mysql>explain select * from payment where amount=3.98 and last_update='2006-02-1522:12:32'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ALL

possible_keys: NULL

         key: NULL

     key_len: NULL

         ref: NULL

        rows: 16086

       Extra: Using where

1 row in set (0.00 sec)



ERROR:

No query specified

(4)僅僅對索引進行查詢

當查詢的列都在索引的字段中時,查詢效率更高。

mysql>explain select *from payment where payment_date='2006-02-14 15:16:03' andamount=3.98\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ref

possible_keys: idx_payment_date

         key: idx_payment_date

     key_len: 8

         ref: const,const

        rows: 8

       Extra: NULL

1 row in set (0.00 sec)



ERROR:

No query specified

mysql>explain select last_update from payment where payment_date='2006-02-1415:16:03' and amount=3.98\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ref

possible_keys: idx_payment_date

         key: idx_payment_date

     key_len: 8

         ref: const,const

        rows: 8

        Extra: Using index

1 row in set (0.00 sec)



ERROR:

No query specified

可以發現第二次查詢extra部分變成了Using index 也就意味着,現在直接訪問索引足夠獲取到所需的數據,不需要通過索引回表,Using index也就是平常說的覆蓋索引掃描。至訪問必須訪問的數據,減少不必要的數據訪問能夠提高效率。

(5)匹配列前綴

mysql>explain select title from film_text where title like 'AFRICAN%'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: film_text

        type: range

possible_keys:idx_title_desc_part,idx_title_description

         key: idx_title_desc_part

     key_len: 32

         ref: NULL

        rows: 1

       Extra: Using where

1 row in set (0.00 sec)



ERROR:

No query specified

EXTRA值爲Using where 表示優化器需要通過索引回表查詢數據。

(6)能夠實現索引匹配部分精確而其他部分進行範圍匹配

mysql> explain select inventory_id from rentalwhere rental_date='2006-02-14 15:16:03' and customer_id>=300 and customer_id<=400\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: rental

        type: ref

possible_keys:rental_date,idx_fk_customer_id

         key: rental_date

     key_len: 5

         ref: const

        rows: 181

       Extra: Using where; Using index

1 row in set (0.00 sec)



ERROR:

No query specified

usingindex表示查詢使用了覆蓋索引掃描

(7)如果列名是索引,那麼使用column_name is null就會使用索引

mysql>explain select * from payment where rental_id is null \G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ref

possible_keys: fk_payment_rental

         key: fk_payment_rental

     key_len: 5

         ref: const

        rows: 5

       Extra: Using index condition

1 row in set (0.00 sec)



ERROR:

No query specified

MySQL中存在索引但是不能使用索引的典型場景

(1)以%開頭的LIKE查詢不能夠利用B-Tree索引

mysql>explain select * from actor where last_name like '%NI%'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: actor

        type: ALL

possible_keys: NULL

         key: NULL

     key_len: NULL

         ref: NULL

        rows: 200

       Extra: Using where

1 row in set (0.00 sec)



ERROR:

No query specified

因爲是B-Tree索引的結構,所以以%開頭查詢自然沒法利用索引,這種情況一般推薦使用全文索引來解決類似問題。或者考慮利用InnoDB的表都是聚簇表的特點,採用輕量級別的解決方式:一般情況下,索引都會比表小,掃描索引要比掃描表更快,而InnoDB表上二級索引idx_last_name實際上存儲字段last_name還有主鍵actor_id,那麼理想的訪問方式是首先掃描二級索引idx_last_name獲得滿足條件last_name like %IN%的主鍵actor_id列表,之後根據主鍵去檢索記錄,避開全表掃描演員表actor產生的大量IO。

(2)數據類型出現隱式轉換的時候也不會使用索引

mysql>explain select * from actor where last_name=1\G;

*************************** 1. row***************************

           id: 1

 select_type: SIMPLE

       table: actor

        type: ALL

possible_keys: idx_actor_last_name

         key: NULL

     key_len: NULL

         ref: NULL

        rows: 200

       Extra: Using where

1 row in set (0.00 sec)

mysql>explain select * from actor where last_name='1'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: actor

        type: ref

possible_keys: idx_actor_last_name

         key: idx_actor_last_name

     key_len: 137

         ref: const

        rows: 1

       Extra: Using index condition

1 row in set (0.00 sec)



ERROR:

No query specified

我們可以看到前者沒有加引號,即便是此列有索引,使用錯誤的數據類型導致沒有使用索引。

(3)符合索引情況下,不滿足最左原則不會使用符合索引

mysql>explain select * from payment where amount=3.89 and last_update='2006-02-1522:12:32'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ALL

possible_keys: NULL

         key: NULL

     key_len: NULL

         ref: NULL

        rows: 16086

       Extra: Using where

1 row in set (0.00 sec)



ERROR:

No query specified

(4)如果MySQL估計使用索引比全表掃描更慢,則不使用索引

mysql> update film_text set title = concat('s',title);
Query OK, 1000 rows affected (0.08 sec)
Rows matched: 1000  Changed: 1000  Warnings: 0

mysql>explain select * from film_text where title like 'S%'\G;

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: film_text

        type: ALL

possible_keys: idx_title_desc_part,idx_title_description

         key: NULL

     key_len: NULL

         ref: NULL

        rows: 1000

       Extra: Using where

1 row in set (0.00 sec)



ERROR:

No query specified

(5)用or分割卡id條件,如果or錢的條件中的列有索引,而後面的列沒有索引,那麼就不會使用索引。

mysql>explain select * from payment where customer_id=203 or amount=3.96\G

*************************** 1. row***************************

          id: 1

 select_type: SIMPLE

       table: payment

        type: ALL

possible_keys: idx_fk_customer_id

         key: NULL

     key_len: NULL

         ref: NULL

        rows: 16086

       Extra: Using where

1 row in set (0.00 sec)

因爲or後面的條件沒有索引,那麼後面的查詢肯定要走全表掃描,存在全表掃描的情況下,就沒必要多一次索引掃描而增加I/O訪問,一次全表掃描就夠了。

3、查看索引使用情況

如果索引正在工作,Headler_read_key的值很高,這個值代表了一個行被索引值讀的次數,很低的值表明增加索引得到的性能改善不高,因爲索引並不經常使用。
這裏寫圖片描述

最後一行Handler_read_rnd_next表示讀下一行的請求數,值越大,則說明索引不正確或者寫入的查詢沒有利用索引,需要建立索引改善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章