问题如下:有一个表,总行数为21万,其中只有一个组合主键索引 primary key (channel_app_id, content_app_id)
create table t_channel_apply
(
channel_app_id varchar(64) default '' not null comment '渠道方appid',
content_app_id varchar(64) default '' not null comment '内容方appid',
apply_state tinyint(2) default 0 not null comment
created_at timestamp default '0000-00-00 00:00:00' not null comment '创建时间',
updated_at timestamp default CURRENT_TIMESTAMP not null comment '更新时间,有修改自动更新',
primary key (channel_app_id, content_app_id)
)
comment '渠道申请信息表';
复制代码
我的查询语句是,只用到了content_app_id
select count(content_app_id) from `t_channel_apply` where `content_app_id` = 'xxx'
复制代码
如我们周知,mysql的组合索引都是左匹配的原则,即假如组合索引(a,b,c)
a,
a,b
a,b,c
c,b,a
c,a,b
b,a
...
//左匹配并且因为索引位置可以随意替换,故以上都会用到索引
b,c
//如果不能左匹配就无法使用索引
复制代码
但我 explain select count(content_app_id) from t_channel_apply where content_app_id = 'xxx',发现这个语句竟然用到了索引,这个真是颠覆了我的认知
ok,开始慢慢分析
首先我注意到,不管我的content_app_id查询值是什么,rows都不会变,固定是86548,这个奇怪的数字
根据以往的知识 explain 中的 rows 的意思是mysql预估要得到结果要查询的行数,然后才会真正执行sql语句,所以这个值只能做参考。
然后我尝试更改语句来对比。
使用 select count(apply_state) from t_channel_apply where content_app_id = 'xxx' and apply_state = 0;
使用 select count(content_app_id) from t_channel_apply where apply_state = 0;,
使用 select count(apply_state) from t_channel_apply where content_app_id = 'xxx';,
以上语句都会全表查找,所以就想到查询条件和查询字段都是索引的情况就会使用到不就符合了覆盖索引的条件吗?
所以得出了结论,就是即使组合索引只用了一部分也能用上索引是对的,前提是符合覆盖索引的条件,原理就是这次执行只需要遍历索引树就可以得到结果
所以可以得出 86548 其实就是索引的行数,mysql预估的时候其实预估查询索引的行数,虽然真正运行并不准确
ps:
在尝试查询中,我得到了一个结果是extra:Impossible WHERE noticed after reading const tables
根据主键查询或者唯一性索引查询,如果这条数据没有的话,它会全表扫描,
然后得出一个结论,该数据不在表中。
对于高并发的库来说,这条数据,会让负载特别的高。