看前提問:table中有多個字段組成的聯合索引(a,b,c),查詢時哪些情況能夠命中索引呢?
話不多說,直接開搞:
數據庫表結構如下:
1 CREATE TABLE `test` ( 2 `id` bigint(11) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵', 3 `class_id` int(4) NOT NULL DEFAULT '0' COMMENT '班級ID', 4 `score` int(4) NOT NULL DEFAULT '0' COMMENT '學分', 5 `total` int(4) NOT NULL DEFAULT '0' COMMENT '總計', 6 PRIMARY KEY (`id`), 7 KEY `idx_class_score_total` (`class_id`,`score`,`total`) COMMENT '測試表' 8 ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='測試表';
可以看到:表中根據class,score,total字段建立聯合索引 idx_class_score_total ,下面開始驗證我們的猜想;
首先我們驗證當根據一個字段查詢數據的情況:
1 EXPLAIN SELECT * FROM test WHERE class = 1;
2 EXPLAIN SELECT * FROM test WHERE score = 1;
3 EXPLAIN SELECT * FROM test WHERE total = 1;

上述結果發現:type有兩種類型:ref和index;
index:這種類型表示mysql會對整個該索引進行掃描。要想用到這種類型的索引,對這個索引並無特別要求,只要是索引,或者某個聯合索引的一部分,mysql都可能會采用index類型的方式掃描。但是呢,缺點是效率不高,mysql會從索引中的第一個數據一個個的查找到最后一個數據,直到找到符合判斷條件的某個索引。所以,上述語句會觸發索引。
ref:這種類型表示mysql會根據特定的算法快速查找到某個符合條件的索引,而不是會對索引中每一個數據都進行一一的掃描判斷,也就是所謂你平常理解的使用索引查詢會更快的取出數據。而要想實現這種查找,索引卻是有要求的,要實現這種能快速查找的算法,索引就要滿足特定的數據結構。簡單說,也就是索引字段的數據必須是有序的,才能實現這種類型的查找,才能利用到索引。
結果:根據一個字段查詢的時候實際上都可以命中索引,只不過ref效率更高,index情況效率不高;
其次我們再來驗證根據任意兩個字段查詢的結果:
1 EXPLAIN SELECT * FROM test WHERE class = 1 AND score = 1;
2 EXPLAIN SELECT * FROM test WHERE class = 1 AND total = 1;
3 EXPLAIN SELECT * FROM test WHERE score = 1 AND total = 1;

上述實踐可得:a_b,a_c可以命中索引,b_c沒有命中索引;
是不是有個問題?為什么a_c也可以命中索引?
我們仔細觀察下sql1和sql2的執行結果,可以發現key_len是不一樣的且ref不一樣,a_c命中索引並不是全命中,只是部分命中索引,命中的是a的索引,所以key_len=4且ref=const;
最后我們看下根據三個字段一起查詢的結果(包括打亂順序);
1 EXPLAIN SELECT * FROM test WHERE class = 1 AND score = 1 AND total = 1;
2 EXPLAIN SELECT * FROM test WHERE total = 1 AND score = 1 AND class = 1;

上述結果發現:兩條sql的執行結果是一致的,所以當根據這三個字段查詢數據時都可以命中索引無論順序;
是不是又有一個疑問?為什么打亂順序也可以命中索引?
mysql中的查詢優化器會糾正這條sql語句該以哪種順序執行效率最高,最后生成真正的執行計划(QEP),盡量利用到索引時查詢順序效率最高。所以mysql查詢優化器最終會以(a_b_c)這種順序查詢,所以b_a_c可以命中索引;
附錄:
各屬性含義:
-
id: 查詢的序列號
-
select_type: 查詢的類型,主要是區別普通查詢和聯合查詢、子查詢之類的復雜查詢
- SIMPLE:查詢中不包含子查詢或者UNION
- 查詢中若包含任何復雜的子部分,最外層查詢則被標記為:PRIMARY
- 在SELECT或WHERE列表中包含了子查詢,該子查詢被標記為:SUBQUERY
-
table: 輸出的行所引用的表
-
type: 訪問類型
- ALL: 掃描全表
- index: 掃描全部索引樹
- range: 掃描部分索引,索引范圍掃描,對索引的掃描開始於某一點,返回匹配值域的行,常見於between、<、>等的查詢
- ref: 使用非唯一索引或非唯一索引前綴進行的查找
- (eq_ref和const的區別:)
- eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵或唯一索引掃描
- const, system: 單表中最多有一個匹配行,查詢起來非常迅速,例如根據主鍵或唯一索引查詢。system是const類型的特例,當查詢- 的表只有一行的情況下, 使用system。
- NULL: 不用訪問表或者索引,直接就能得到結果,如select 1 from test where 1
-
key: 顯示MySQL實際決定使用的索引。如果沒有索引被選擇,是NULL
-
key_len: 使用到索引字段的長度
注:key_len顯示的值為索引字段的最大可能長度,並非實際使用長度,即key_len是根據表定義計算而得,不是通過表內檢索出的。 -
ref: 顯示哪個字段或常數與key一起被使用
-
rows: 這個數表示mysql要遍歷多少數據才能找到,表示MySQL根據表統計信息及索引選用情況,估算的找到所需的記錄所需要讀取的行數,在innodb上可能是不准確的
-
Extra: 執行情況的說明和描述。包含不適合在其他列中顯示但十分重要的額外信息。
-
Using index:表示使用索引,如果只有 Using index,說明他沒有查詢到數據表,只用索引表就完成了這個查詢,這個叫覆蓋索引。
-
Using where:表示條件查詢,如果不讀取表的所有數據,或不是僅僅通過索引就可以獲取所有需要的數據,則會出現 Using where。