定義
不同於對某列建立索引,可以同時對多個列建立索引,也稱複合索引、聯合索引。
測試表
CREATE TABLE `test` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`c1` int(10) NOT NULL COMMENT 'c1',
`c2` int(10) NOT NULL COMMENT 'c2',
`c3` int(10) NOT NULL COMMENT 'c3',
`c4` int(10) NOT NULL COMMENT 'c4',
PRIMARY KEY (`id`) USING BTREE,
KEY `IDX_C1C2C3` (`c1`,`c2`,`c3`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
其中對c1、c2、c3建立組合索引。
存儲結構
單索引的存儲結構很好理解,非葉子節點中存儲單索引的值。當有多個列組成組合索引時,存儲結構如圖,摘自《高性能Mysql,第三版》
組合索引對多個值進行排序是依照定義時索引的的順序。這裏按照c1、c2、c3的順序定義,即在存儲時先按照c1排序,c1相同時按c2進行排序,c2相同時按c3排序。所以查詢時想使索引中的c2列生效,那麼條件中也要有c1。想使c3列生效,c1和c2必須也要有。這也就是常說的組合索引最左前綴匹配。
測試
- 全匹配
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 = 222 and c3 = 333
key_len爲12,正好爲三個字段的長度,證明三個字段的索引全部生效了。
where後的查詢順序不影響組合索引的使用
EXPLAIN SELECT * FROM test WHERE c2 = 222 and c1 = 111 and c3 = 333
只要包含了需要的索引,explain的結果和上面的一樣,不再貼出。
- 不含c1
EXPLAIN SELECT * FROM test WHERE c2 = 222 and c3 = 333;
搜索條件不含c1,發現走的全表掃描,組合索引沒有生效。
- 不含c2
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c3 = 333
雖然用到了聯合索引,但是實際使用長度只有4,也就是隻用了c1。
- 不含c3
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 = 222
使用了聯合索引,實際使用長度爲8,c1和c2都用上了。
- 匹配列
這裏把c2改成varchar(10)
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 LIKE '22' and c3 = 333
key_len爲40,c1、c2、c3全部使用了。
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 LIKE '%22' and c3 = 333
key_len爲4,只用到了c1。結合組合索引的存儲也可以理解,匹配前綴的時候,可以用到排序,但是匹配後綴,排序就失效了,所以從c2這裏就斷掉了。
- 匹配範圍值
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 < 22 and c3 = 33
key_len爲8,c1和c2生效,範圍搜索後的c3失效了。
- 其他失效情況
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 + 2 = 22 and c3 = 33
計算式會使該列索引失效,這裏只有c1生效了。
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 != 22 and c3 = 33
!=使得整個組合索引失效。
再次將c2改爲varchar(10)
EXPLAIN SELECT * FROM test WHERE c1 = 111 and c2 not like '22' and c3 = 33
只有c1生效了。
EXPLAIN SELECT * FROM test WHERE c1 = 111 and UPPER(c2) = '222' and c3 = 33
只有c1生效了。
參考
- 《高性能mysql,第三版》
- http://blog.codinglabs.org/articles/theory-of-mysql-index.html