之前在网上看到过很多关于mysql联合索引最左前缀匹配的文章,最近面试时和面试官交流,发现面试官大都理解有误,导致于误人子弟。今天就来整理下。
最左前缀匹配原则
在mysql建立联合索引时会遵循最左前缀匹配的原则,即最左优先,在检索数据时从联合索引的最左边开始匹配,示例:
对列col1、列col2和列col3建一个联合索引
KEY test_col1_col2_col3 on test(col1,col2,col3);
查询的时候
SELECT * FROM test WHERE col1="1" AND clo2="2" AND clo4=|"4"
上面这个查询语句执行时会依照最左前缀匹配原则,检索时会使用索引(col1,col2)进行数据匹配。
网上认为,对于(col1,col2,col3)这种查询,只有
SELECT * FROM test WHERE col1="1" ;SELECT * FROM test WHERE col1="1" AND col2="2";SELECT * FROM test WHERE col1="1" AND col2="2" AND col3="3" ;
这三种形式能用到索引。这就是网上对联合索引的简介。
那事实呢?
上代码
我使用最新的MySQL 8.0.12版本,建表语句如下:
CREATE TABLE `itest` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(100) DEFAULT NULL COMMENT '姓名', `age` int(11) DEFAULT NULL COMMENT '年龄', `power` int(11) DEFAULT NULL COMMENT '权限', `address` varchar(100) DEFAULT NULL COMMENT '地址', `addtime` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `itest_udx` (`age`,`power`,`addtime`) USING BTREE) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8
这里我们建了itest_udx这个联合索引,添加几条数据试试。
INSERT INTO itest (name,age,power,address,addtime) VALUES ('朱元璋',65,12,'hebei','2019-05-14 08:00:00.000'),('李白',33,3,'吐鲁番','2019-05-14 16:00:00.000'),('tom',14,5,'美国','2019-05-02 16:00:00.000'),('小家伙',12,7,'湖北','2006-11-14 16:00:00.000');
好了,我们来测试下。使用EXPLAIN就能看到SQL是否使用索引了。
explain SELECT * FROM itest where age=11;explain SELECT * FROM itest where age=12 and power=5;
为了方便描述,我们把这三个索引字段称为abc。对于a和ab这种形式,可以看到,都使用了索引,没问题。
联合索引
如果是只查 b呢,有人说按照最左匹配原则是不会用到索引的,有人说会,我们来看看结果
b不会用到索引
事实证明了不会用到索引,那只查c这个字段会用到索引么?既然只查b用不上,那么只查c应该也用不上,还是用代码验证吧
c不会用到索引
果然,单独查c也不会用到索引。
到这里我们总结下,对于abc的联合索引,WHERE条件中单独查a字段会用上索引,单独查b和c字段不会用上索引。
那我们试试ba这种查询,理论上按照最做匹配原则肯定不会用到索引,事实呢?
ba会用到索引
让人大跌眼镜,ba居然能用到索引,简直让人不可思议!
那试一下bc,看看结果呢?这里就不贴图了,结论是bc用不到索引。
那其他组合呢,我就不试了,我直接贴结果吧
结果有没有让你震惊呢?
那么abc这种组合呢,按照排列组合原理,abc这种组合也有6种组合。根据网上的说法,只有abc组合能用到索引,事实是这样的吗?
结果同样跟网上的大相径庭,全部都会用到索引。
结论
看图就好了
联合索引使用情况
是不是跟你预料中的完全不一样?
刚才说了,我使用的MySQL版本是8.x,而且数据量不多。也许不同的版本,甚至数据量多少也会导致索引使用情况不一样,但是,那些信誓旦旦就认定联合索引只有a,ab,abc这三种组合能用到索引的,进来挨打。