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表示讀下一行的請求數,值越大,則說明索引不正確或者寫入的查詢沒有利用索引,需要建立索引改善。