MySQL索引MySQL(总)

where条件中出现了(不等于/多重范围)就一定不走索引了吗

2019-02-14  本文已影响2人  飞翔的Tallgeese

今天朋友抛了一个群里的讨论给我,如下图

这题我选择了A,B,因为我认为A和B都不满足最左;最后朋友实际建表的测试结果如下(也就是说答案应该是A,B,D)

按理说不等于不走索引应该是SQL优化的常识了,为什么我在选择的时候排除了D呢?因为2个月前我遇到了这样一个语句

SELECT MAX(id)

FROM a

WHERE user_id != '18'

AND '2019-02-13 00:00:00' >= create_time

GROUP BY user_id;

因为这个语句的关系,我一度认为在5.7.25版本里不等于也已经开始智能的走索引了;经过这个选择题,决定仔细探索一下MySQL的不等于


对原语句进行不同的改写

备注:

①索引idx_uid_ct为user_id和create_time的联合索引idx_uid_ct(user_id,create_time)

②后面为了测试,特意删除了idx_ct(create_time)

③id为表的主键列

eg1.去除group by的影响,type为range,覆盖索引

eg2.去除max函数,type为range,覆盖索引

eg3.查询项改为create_time、user_id、id中的任意1到3个,type为range,覆盖索引

eg4.查询项改为*或者是索引列(user_id,create_time,id)之外的任意值,索引前导列存在不等于,不走索引

eg5.查询项改为*或者是索引列(user_id,create_time,id)之外的任意值,索引的非前导列存在不等于,走索引,extra为ICP

eg6.查询项改为create_time或者user_id,where条件改为只有非前导列create_time,不满足idx_uid_ct最左匹配规则,type为index,覆盖索引

eg7.查询项改为索引列(user_id,create_time,id)之外的任意值,where条件将user_id的不等于改为等于,create_time条件不变,type为range,Extra为ICP

eg8.查询项改为索引列(user_id,create_time,id)之外的任意值,where条件将user_id的不等于改为大于或者小于,create_time条件不变,是否走索引需要优化器进行判断

eg9.查询项改为索引列(user_id,create_time,id)之中的任意1-3个值,where条件将user_id的不等于改为大于或者小于,create_time条件不变,type为range,覆盖索引


结论

①对比上面的9个eg可以看出,当查询列位于主键或者索引列范围内的时候(或者像原语句那样是主键/索引列的分组函数),整个语句会直接走覆盖索引,这种情况下无论where条件里有没有不等于都不会有影响

②参考eg4可知查询列在索引列或主键列的范围之外,索引前导列存在不等于,不走索引

③参考eg5可知,查询列在索引列或主键列的范围之外,索引非前导列存在不等于,仍然走索引,此时需要回表,因此extra为ICP。

④对比eg5和eg7,两者的执行计划中的extra均为ICP,但key_len和type不同;eg5的非前导列在where条件中存在不等于,因此联合索引中的非前导列create_time并没有被利用到,所以key_len只有99;eg7的非前导列条件where条件中是一个范围,由于是where条件中的第一个范围,因此这个范围可以走索引,所以key_len为105;eg5和eg7之所以都是ICP是因为查询列user_name需要回表

⑤参考eg9,当满足覆盖索引条件的时候,即使在where条件中有双范围,仍然会被全部利用到

⑥参考eg8,不满足覆盖索引条件时,where条件中只有第一个范围能被索引利用到,是否走索引优化器自身会做权衡,当取值量太大时索引反而不如全表

⑦参考eg6,满足覆盖条件时,即使where条件不满足索引的最左规则,语句仍然走了索引只不过type为极为低效的index(全索引扫描),extra显示此时仍然是覆盖索引

上一篇下一篇

猜你喜欢

热点阅读