原因:
在一次和同事討論mysql聯合索引的面試題時出現了爭議。主要問題是:a、b、c三個欄位作為聯合索引,b、c;和a、c情況到底會不會命中索引?
網上查閱相關部落格發現很多答案不一樣,於是我乾脆親手操作實驗一下 ,我使用的mysql版本是5.6
一:建立表
為了更直接貼合面試題,欄位直接用AA,BB,CC表示
create table IF NOT EXISTS TEST_COMPOSITE_INDEX
(
`TID` BIGINT NOT NULL AUTO_INCREMENT,
`AA` VARCHAR(50) NOT NULL DEFAULT '' ,
`BB` VARCHAR(50) NOT NULL DEFAULT '',
`CC` VARCHAR(50) NOT NULL DEFAULT '',
`DD` VARCHAR(50) NOT NULL DEFAULT '',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`TID`),
KEY `index_comp` (`AA`,`BB`,`CC`)
)ENGINE=InnoDB DEFAULT CHARACTER SET utf8mb4;
sql中我們有兩個索引,一:主鍵TID,二:AA、BB、CC組成的聯合索引:index_comp
二:插入資料
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A1','B1','C1','D1');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A2','B2','C2','D2');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A3','B3','C3','D3');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A4','B4','C4','D4');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A5','B5','C5','D5');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A6','B6','C6','D6');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A7','B7','C7','D7');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A8','B8','C8','D8');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A9','B9','C9','D9');
insert into TEST_COMPOSITE_INDEX (AA,BB,CC,DD)VALUE('A10','B10','C10','D10');
三:檢視執行計劃
我們分別檢視以下執行計劃:
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.tid='3' ;
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA='A1';
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.BB='B1';
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.CC='C1' ;
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA='A1' AND tci.CC='C1';
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.BB='B1' AND tci.CC='C1';
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA='A1' AND tci.BB='B1';
以下結果按順序展示,我們一個個看:
第一個:
首先第一個使用主鍵查詢,可以看到執行計劃中說明使用了主鍵,掃描行數是1,非常高效
第二個:
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA='A1';
可以看到同樣使用了索引,這次使用的是我們建立的聯合索引索引:index_comp。注意key_len:索引使用的位元組數,這個後面會用作對比
第三、四個:
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.BB='B1';
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.CC='C1' ;
只是使用BB或者CC欄位,並不會使用索引,掃描行數10
第五個:
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA='A1' AND tci.CC='C1';
這裡是我們有爭議的一個點,可以看到的確使用了索引,但是key_len索引使用的位元組數是152和第二個語句只用A查詢,使用的位元組數是一致的,所以這句的結論是:A、C查詢的時候雖然使用了索引,實際其實只用了A,而不是AC
第六個:
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.BB='B1' AND tci.CC='C1';
顯而易見,B、C查詢的時候並不會使用索引
第七個
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA='A1' AND tci.BB='B1';
最後是正常使用的聯合索引,遵循最左匹配,索引使用的位元組數:304,正好是之前命中單個欄位的雙倍
最後補充說明面試中可能碰到的問題:
如果是組合索引,且遵循最左匹配,如果其中有欄位是範圍查詢,那麼:命中的欄位只會到範圍查詢那個欄位,比如:
EXPLAIN select * FROM TEST_COMPOSITE_INDEX tci where tci.AA>'A1' AND tci.BB='B1';
那麼這次查詢使用的索引欄位只有:AA,而不會使用BB