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,說明使用索引的第一列
場景二:mobile和name加起來是索引的左前綴
explain select * from teacher where mobile = '18600660088' and name = 'kevin';
說明:ref列出現了兩個const,說明使用索引的前綴mobile和name
場景三:mobile、name和birthday加起來是索引的左前綴
explain select * from teacher where birthday = '2019-01-01' and name = 'kevin' and mobile = '18600660088';
說明:ref列出現了三個const,說明使用索引的第一列、第二列和第三列
注意:mysql優化器會自動調整mobile、name、birthday在查詢條件中出現的順序以匹配索引
場景四:只有mobile是前綴,中間跳過了索引中第二列(name),birthday不使用索引
explain select age from teacher where mobile = '18600660088' and birthday = '2019-01-01';
說明:ref列只出現了一個const,說明使用索引的前綴mobile部分
場景五:mobile和name加起來是索引的前綴,而且%位於模糊查詢後綴
explain select * from teacher where mobile = '18600660088' and name like 'kevin%';
說明:key_len是246與場景二一致,只使用了索引的前綴部分
場景六:mobile和name加起來是索引的前綴,而且%位於模糊查詢的前綴
explain select * from teacher where mobile = '18600660088' and name like '%kevin';
說明:mobile字段用到了索引,name不使用索引
場景七:mobile是索引的最左前綴,而且使用了範圍查詢
explain select * from teacher where mobile > '18600660088' and name = 'kevin' and birthday = '2019-01-01';
說明:key_len是51與場景六一致,只使用了索引前綴mobile,name和birthday不使用索引
結論:索引從左往右匹配,遇到範圍查詢後中止匹配
場景八:name位於組合索引的中間,而且%位於模糊查詢後綴
explain select * from teacher where mobile = '18600660088' and name like 'kevin%' and birthday = '2019-01-01';
說明:key_len顯示251說明跟場景三一致,使用到了整個組合索引
結論:%位於模糊查詢後綴不影響索引的使用,若是是組合索引能夠繼續往右匹配
場景一:缺乏前綴mobile字段
explain select * from teacher where name = 'kevin chen';
說明:type列顯示ALL表示全表掃描,MySQL 從頭至尾掃描整張表查找行
場景二:缺乏mobile和name組合的前綴字段
explain select * from teacher where birthday = '2019-01-01';
說明:type列顯示ALL表示全表掃描,MySQL從頭至尾掃描整張表查找行
場景三:like模糊匹配%位於前綴
explain select * from teacher where mobile like '%18600660088';
說明:type列顯示ALL表示全表掃描,MySQL從頭至尾掃描整張表查找行
場景四:索引列進行了函數運算
explain select * from teacher where trim(mobile) = '18600660088';
說明:正確的作法是在等號的右邊作數據運算或函數運算
場景五:字段類型不匹配
explain select * from teacher where mobile = 18600660088;
說明:mobile是varchar類型,18600660088是整數
場景一:須要的數據都在索引中,走覆蓋索引
explain select mobile,name,birthday from teacher where mobile = '18600660088';
說明:Extra列顯示附加信息,Using index表示使用覆蓋索引
場景二:查詢的age字段不在索引中,不能使用覆蓋索引
explain select age from teacher where mobile = '18600660088';
場景一:查詢字段和排序字段是同一個索引
explain select id,mobile,name,birthday from teacher order by mobile,name,birthday;
說明:extra顯示Using index表示使用了覆蓋索引,二級索引中隱含了聚簇索引(主鍵)
場景二:多個排序字段位於多個不一樣索引
explain select * from teacher order by mobile, email;
說明:mobil和email屬於不一樣索引,Using filesort說明使用外部排序,不能用索引排序
場景三:多個排序字段不符合最左前綴匹配原則
explain select id,mobile,name,birthday from teacher order by mobile, birthday;
說明:查詢用了索引,排序跳過了組合索引中間字段name,extra顯示Using filesort
場景四:查詢含索引外字段,用索引掃描後再回表查詢比直接掃表成本更高,因此沒使用索引
explain select * from teacher order by mobile,name,birthday;
說明:extra顯示Using filesort表示使用了外部排序
場景五:查詢條件字段和排序字段組合起來符合索引最左前綴
explain select * from teacher where mobile='18600660088' order by name;
場景一:分組字段(多字段組合)屬於索引最左前綴
explain select email, age, name from teacher group by email, age, name;
說明:email、age和name組合起來符合最左前綴,使用索引idx_two,extra顯示Using index
explain select distinct email, age, name from teacher;
說明:這裏distinct字段組合起來一樣符合索引最左前綴,使用索引idx_two
場景二:min()/max()函數做用於同一列,而且緊跟屬於同一索引的分組字段
explain select email, min(age), max(age) from teacher group by email;
說明:email是分組字段,age是函數做用字段,email和age組合起來符合idx_two最左前綴
場景三:count(distinct)、avg(distinct)和sum(distinct)組合起來符合最左前綴
explain select count(distinct email), sum(distinct age) from teacher;
說明:avg(distinct)和sum(distinct)中distinct只適用單個字段
場景四:count(distinct),distinct適用於多個字段
explain select count(distinct email, age) from teacher;
說明:extra顯示Using index for group-by說明使用鬆散索引掃描(Loose Index Scan)
場景五:缺乏組合索引中間部分,不能使用索引排序
explain select email, name from teacher group by email, name;
說明:分組字段缺乏idx_two索引age部分,extra顯示Using filesort說明使用外部排序
場景六:多個分組字段不屬於同一個索引
explain select email, age, birthday from teacher group by email, age, birthday;
說明:birthday不屬於idx_two索引,顯示Using filesort
場景七:緊湊索引掃描(Tight Index Scan)
explain select email, age, name from teacher where age = 18 group by email, name;
說明:分組字段缺乏了完整索引中間部分,但由查詢條件 age = 18 補充了這部分常量
場景八:緊湊索引掃描(Tight Index Scan)
explain select email, age, name from teacher where email = 'kevin@qq.com' group by age, name;
說明:分組字段不以索引最左前綴開始,但查詢條件 email='kevin@qq.com' 提供了這部分常量
How MySQL Uses Indexes
Multiple-Column Indexes
ORDER BY Optimization
GROUP BY Optimization
EXPLAIN Output Format