mysql innodb 索引使用指南

mysql innodb 索引使用指南html

索引相關概念

  1. 聚簇索引(clustered index)
    使用innodb引擎時,每張表都有一個聚簇索引,好比咱們設置的主鍵就是聚簇索引
    特色:查詢數據特別快,由於聚簇索引和行數據存儲在磁盤的同一頁,這樣能夠減小磁盤I/O操做次數(影響mysql性能的重要因素)
    注意:主鍵索引應該儘可能簡短
  2. 二級索引(secondary index)
    除了聚簇索引外的其餘索引叫作二級索引(輔助索引),好比咱們給除主鍵外其餘字段建立的索引
    特色:二級索引裏面存儲了聚簇索引,最後要經過聚簇索引找到行數據。可見,聚簇索引的效率會影響其餘索引
  3. 覆蓋索引(covering index)
    索引包含了查詢語句須要的全部數據,這種索引稱爲覆蓋索引
    特色:索引的葉子節點中已經包含要查詢的數據,不須要回表操做因此很快(減小了磁盤I/O操做次數)
  4. 組合索引(multiple-column index)
    組合索引也稱爲複合索引(聯合索引),是指把多個字段組合起來建立一個索引(最多16個字段)
    特色:遵循最左前綴匹配原則
  5. 最左前綴匹配原則(leftmost prefix principle)
    mysql會從左向右匹配直到遇到不能使用索引的條件(>、<、!=、not、like模糊查詢的%前綴)才中止匹配
    設想用a,b,c字段建立一個組合索引(a,b,c)
    因爲a是索引的最左邊前綴,因此where條件中必須匹配字段a,mysql優化器纔會用到這個索引
    在匹配字段a的前提下,才能匹配字段b
    在匹配字段a的前提下,而且匹配字段b,而後才能匹配字段c

使用explain查看執行計劃

explain命令用來查看select語句執行計劃,確認該SQL語句有沒有使用索引,是否作全表掃描,是否使用覆蓋索引等mysql

id select_type table type possible_keys key key_len ref rows Extra

type:表明數據訪問類型(由左至右,由最差到最好)
| All | index | range | ref | eq_ref | const | system | null |sql

possible_keys:表示哪些索引可能有利於高效的查找服務器

key:顯示mysql決定採用哪一個索引來優化查詢mysql優化

key_len:顯示mysql在索引裏使用的字節數函數

ref:顯示了以前的表在key列記錄的索引中查找值所用的列或常量性能

rows:爲了找到所需的行大體須要讀取的行數測試

extra:表示額外的信息(左邊較差,右邊較好)
| Using filesort| Using temporary | Using where | Using index condition | Using index |
Using index:使用了覆蓋索引,速度很快,限於查詢字段都位於同一個索引中的場景
Using index condition:表示使用了ICP優化(Index Condition Pushdown),能減小引擎層訪問基表的次數和MySQL Server訪問存儲引擎的次數
Using where:表示在存儲引擎檢索行後mysql服務器再進行過濾
Using filesort:返回結果前須要作一次外部排序(內存或硬盤),速度慢應該儘可能避免
Using temporary:在對查詢結果排序時會使用一個臨時表,速度慢優化

建立一張測試表

使用InnoDB引擎建立teacher表,id設爲自增主鍵
mobile、name和birthday創建第一個組合索引idx_one(注意三個字段在索引中順序)
email、age和name字段創建第二個組合索引idx_two(一樣注意順序)spa

CREATE TABLE `teacher` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(64) DEFAULT NULL,
  `birthday` timestamp NULL DEFAULT NULL,
  `email` varchar(32) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  `mobile` varchar(16) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_one` (`mobile`,`name`,`birthday`),
  KEY `idx_two` (`email`,`age`,`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

符合最左前綴場景

場景一:mobile是索引的左前綴

explain select * from teacher where mobile = '18600660088';

說明:ref列只出現了一個const,說明使用索引的第一列
clipboard.png

場景二:mobile和name加起來是索引的左前綴

explain select * from teacher where mobile = '18600660088' and name = 'kevin';

說明:ref列出現了兩個const,說明使用索引的前綴mobile和name
clipboard.png

場景三:mobile、name和birthday加起來是索引的左前綴

explain select * from teacher where birthday = '2019-01-01' and name = 'kevin' and mobile = '18600660088';

說明:ref列出現了三個const,說明使用索引的第一列、第二列和第三列
注意:mysql優化器會自動調整mobile、name、birthday在查詢條件中出現的順序以匹配索引
clipboard.png

場景四:只有mobile是前綴,中間跳過了索引中第二列(name),birthday不使用索引

explain select age from teacher where mobile = '18600660088' and birthday = '2019-01-01';

說明:ref列只出現了一個const,說明使用索引的前綴mobile部分
clipboard.png

場景五:mobile和name加起來是索引的前綴,而且%位於模糊查詢後綴

explain select * from teacher where mobile = '18600660088' and name like 'kevin%';

說明:key_len是246與場景二一致,只使用了索引的前綴部分
clipboard.png

場景六:mobile和name加起來是索引的前綴,而且%位於模糊查詢的前綴

explain select * from teacher where mobile = '18600660088' and name like '%kevin';

說明:mobile字段用到了索引,name不使用索引
clipboard.png

場景七:mobile是索引的最左前綴,而且使用了範圍查詢

explain select * from teacher where mobile > '18600660088' and name = 'kevin' and birthday = '2019-01-01';

說明:key_len是51與場景六一致,只使用了索引前綴mobile,name和birthday不使用索引
結論:索引從左往右匹配,遇到範圍查詢後中止匹配
clipboard.png

場景八:name位於組合索引的中間,而且%位於模糊查詢後綴

explain select * from teacher where mobile = '18600660088' and name like 'kevin%' and birthday = '2019-01-01';

說明:key_len顯示251說明跟場景三一致,使用到了整個組合索引
結論:%位於模糊查詢後綴不影響索引的使用,若是是組合索引能夠繼續往右匹配
clipboard.png

不使用索引的場景

場景一:缺乏前綴mobile字段

explain select * from teacher where name = 'kevin chen';

說明:type列顯示ALL表示全表掃描,MySQL 從頭至尾掃描整張表查找行
clipboard.png

場景二:缺乏mobile和name組合的前綴字段

explain select * from teacher where birthday = '2019-01-01';

說明:type列顯示ALL表示全表掃描,MySQL從頭至尾掃描整張表查找行
clipboard.png

場景三:like模糊匹配%位於前綴

explain select * from teacher where mobile like '%18600660088';

說明:type列顯示ALL表示全表掃描,MySQL從頭至尾掃描整張表查找行
clipboard.png

場景四:索引列進行了函數運算

explain select * from teacher where trim(mobile) = '18600660088';

說明:正確的作法是在等號的右邊作數據運算或函數運算
clipboard.png

場景五:字段類型不匹配

explain select * from teacher where mobile = 18600660088;

說明:mobile是varchar類型,18600660088是整數
clipboard.png

覆蓋索引

場景一:須要的數據都在索引中,走覆蓋索引

explain select mobile,name,birthday from teacher where mobile = '18600660088';

說明:Extra列顯示附加信息,Using index表示使用覆蓋索引
clipboard.png

場景二:查詢的age字段不在索引中,不能使用覆蓋索引

explain select age from teacher where mobile = '18600660088';

clipboard.png

使用索引排序

場景一:查詢字段和排序字段是同一個索引

explain select id,mobile,name,birthday from teacher order by mobile,name,birthday;

說明:extra顯示Using index表示使用了覆蓋索引,二級索引中隱含了聚簇索引(主鍵)
clipboard.png

場景二:多個排序字段位於多個不一樣索引

explain select * from teacher order by mobile, email;

說明:mobil和email屬於不一樣索引,Using filesort說明使用外部排序,不能用索引排序
clipboard.png

場景三:多個排序字段不符合最左前綴匹配原則

explain select id,mobile,name,birthday from teacher order by mobile, birthday;

說明:查詢用了索引,排序跳過了組合索引中間字段name,extra顯示Using filesort
clipboard.png

場景四:查詢含索引外字段,用索引掃描後再回表查詢比直接掃表成本更高,因此沒使用索引

explain select * from teacher order by mobile,name,birthday;

說明:extra顯示Using filesort表示使用了外部排序
clipboard.png

場景五:查詢條件字段和排序字段組合起來符合索引最左前綴

explain select * from teacher where mobile='18600660088' order by name;

clipboard.png

使用索引分組

場景一:分組字段(多字段組合)屬於索引最左前綴

explain select email, age, name from teacher group by email, age, name;

說明:email、age和name組合起來符合最左前綴,使用索引idx_two,extra顯示Using index
clipboard.png

explain select distinct email, age, name from teacher;

說明:這裏distinct字段組合起來一樣符合索引最左前綴,使用索引idx_two
clipboard.png

場景二:min()/max()函數做用於同一列,而且緊跟屬於同一索引的分組字段

explain select email, min(age), max(age) from teacher group by email;

說明:email是分組字段,age是函數做用字段,email和age組合起來符合idx_two最左前綴
clipboard.png

場景三:count(distinct)、avg(distinct)和sum(distinct)組合起來符合最左前綴

explain select count(distinct email), sum(distinct age) from teacher;

說明:avg(distinct)和sum(distinct)中distinct只適用單個字段
clipboard.png

場景四:count(distinct),distinct適用於多個字段

explain select count(distinct email, age) from teacher;

說明:extra顯示Using index for group-by說明使用鬆散索引掃描(Loose Index Scan)
clipboard.png

場景五:缺乏組合索引中間部分,不能使用索引排序

explain select email, name from teacher group by email, name;

說明:分組字段缺乏idx_two索引age部分,extra顯示Using filesort說明使用外部排序
clipboard.png

場景六:多個分組字段不屬於同一個索引

explain select email, age, birthday from teacher group by email, age, birthday;

說明:birthday不屬於idx_two索引,顯示Using filesort
clipboard.png

場景七:緊湊索引掃描(Tight Index Scan)

explain select email, age, name from teacher where age = 18 group by email, name;

說明:分組字段缺乏了完整索引中間部分,但由查詢條件 age = 18 補充了這部分常量
clipboard.png

場景八:緊湊索引掃描(Tight Index Scan)

explain select email, age, name from teacher where email = 'kevin@qq.com' group by age, name;

說明:分組字段不以索引最左前綴開始,但查詢條件 email='kevin@qq.com' 提供了這部分常量
clipboard.png

參考資料

How MySQL Uses Indexes
Multiple-Column Indexes
ORDER BY Optimization
GROUP BY Optimization
EXPLAIN Output Format

mysql innodb 索引使用指南

相關文章
相關標籤/搜索