当前mysql版本是5.7.14,我们约定在 InnoDB 存储引擎下分析该问题。
说起联合索引必然绕不开“最左原则”。其实这个原则不是完全按照它说的那样。
首先搞一个环境:
create table test (
`id` bigint(20) NOT NULL COMMENT 'ID',
`closing_num` varchar(2) NOT NULL COMMENT '序号',
`invoice_code` varchar(50) NOT NULL DEFAULT '' COMMENT '单号',
`invoice_type` int(11) NOT NULL COMMENT '类型',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
加一个索引
alter table test add index `invoice_code_uindex` (`invoice_code`,`closing_num`,`invoice_type`);
sql1:explain select * from test where invoice_code = '123' and invoice_type = 2;这个大家应该知道这个是走索引的,
按照“最左原则”,select * from test where invoice_type = 2 and invoice_code = '123';这句话应该是不会走索引的。但是:
sql2:explain select * from test where invoice_type = 2 and invoice_code = '123';得到如下结果:
这里是走了索引的。我不知道是不是以前版本的mysql不支持还是怎样,可能是现在的mysql解释器变厉害了,这种它可以让他走索引。但是请注意这里的key_len是152(50*3+2),如果你看过博主另外一篇关于key_len的文章就会知道,这个key_len代表的意思,没错,就是上面那个语句只是使用了invoice_code字段作为索引字段。这就说明sql1与sql2都是只是使用了invoice_code字段作为索引。
sql3:explain select * from test where invoice_type = 2 and closing_num = '01';这个相信大家应该知道这个是不走索引的。
这里得出结论1:联合索引必须有索引的第一个字段,而不必在乎字段条件的顺序。
sql4:explain select id, invoice_code, invoice_type,closing_num from test where invoice_type = 2 and closing_num = '01';那这条语句会不会走索引呢?不走索引吗?
看到没,这个是走索引的。是不是打脸了?请注意,这里的key_len是164(2*3+2+50*3+2+4),说明它是走所有字段的,那么这个是为什么呢?以下这个是个人观点,欢迎各位拍砖,这里涉及到一个回表的概念,说明mysql解析器在发现联合索引的非最左字段作为条件且返回列都是索引字段时,它就走索引了,而且是所有字段的,而如果返回值里面有非索引字段,那么直接全表扫了,有点类似超过全表30%数据时,直接全表扫一样。但是如果你细心你就会发现,这里id也不是联合索引的字段啊,为什么也会走索引,这是因为mysql主键是个特殊的东西,很多规则对它可以无视,即你不必关心这个字段。
最终结论:
1、当创建(a,b,c)联合索引时,相当于创建了(a)单列索引,(a,b)联合索引以及(a,b,c)联合索引 。
2、索引列在where条件有最左列的时候顺序不在意,有些其实是走(a)单列索引;如果where条件有a,b的话那么是走(a,b)联合索引。
3、还有一种情况,就是关于查询返回列如果是只有索引列(主键列除外)也是会走索引的。