本文根据一个案例介绍MYSQL索引失效的原因,并且顺便介绍索引的特性
案例
场景
问题
经过explain 后,发现并没有命中索引,大家想想为啥?
结论
mysql中 like 后不能紧跟%,这样索引会失效,因为不知道如何查找索引。
索引的特性
1. 回表
当二级索引去找叶子节点,然后根据叶子节点中主键id去找主键索引,此时找到数据行,称为回表。
回到主键索引树搜索的过程,叫做回表。
2. 如何避免回表?
覆盖索引: 如果我们把被选择的字段加入到where 字段的索引中,无需去主键索引中找数据了。直接通过二级索引就能拿到数据。
目的: 减少搜索树的次数,提升查询性能。
2.1 最左前缀原则
联合索引的最左N个字段,也可是字符串索引的最左M个字符。
B+树就是采用索引的最左前缀原则,来定位记录的.
联合索引: 其实就是多个字段组成的组合索引.
覆盖索引:如果查询条件使用的是普通索引(或是联合索引的最左原则字段),查询结果是联合索引的字段或是主键,
不用回表操作,直接返回结果,减少IO磁盘读写读取数据.
小案例:
请问已经有了一个name_age联合索引,还需要一个name索引吗?
alter table table_name add index idx_name_age(name, age);
('李四', 22), ('王五',33),('张三', 10),('张三', 10),('张三',20),('六六',40)
不需要?为哈?
因为索引中的索引项是根据索引定义中的字段顺序排序的。
此时
select * from user where name = '张三'; 此时就会命中idex_name_age
select * from user where name like '张%'; 也会, 但是'%三'会使命中索引失效
2.2 建立联合索引的原则
- 如果通过调整顺序,可以少维护一个索引,那么这个顺序就可以优先考虑. 如果有name_age, age此时我看第二个.
- 空间问题, 如果name字段是比age大的,新建name_age, age两个索引. 因为age查询时,无法命中name_age。
- 所以,还是那句话,索引的索引项是根据索引定义中的字段顺序排序的。
- 可以通过索引选择性来看name和age的顺序
- select count(distinct name)/count(name), count(distinct age)/count(age) from table\G.
3. 索引下推
联合索引idx_name_age 假如一个select * from table where name like 'hello%' and age >10检索.
- MySQL5.6版本之前,会对匹配的数据进行回表查询. 也是只会命中name开头hello的数据,然后回表.
- 5.6版本之后,也是只会命中name开头hello的数据C,并且会先过滤C中的age<10(不满足条件的)数据,然后在进行查询,
减少回表率,提升检索速度。