索引优化原则——生效与失效

CREATE TABLE `student` (
  `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
  `name` varchar(24) COLLATE utf8mb4_bin NOT NULL COMMENT '姓名',
  `age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
  `class` varchar(20) COLLATE utf8mb4_bin NOT NULL COMMENT '班级',
  `in_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '入学时间',
  PRIMARY KEY (`id`),
  KEY `idx_name_age_class` (`name`,`age`,`class`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;


INSERT INTO test.student (id, name, age, class, in_date) VALUES (1, '张三', 8, '一年一班', '2022-09-19 21:42:14');
INSERT INTO test.student (id, name, age, class, in_date) VALUES (2, '李四', 9, '二年一班', '2022-09-19 21:42:16');
INSERT INTO test.student (id, name, age, class, in_date) VALUES (3, '王五', 10, '三年一班', '2022-09-19 21:42:29');

1.全值匹配:符合最左前缀原则,都会走索引,区别在于使用索引列个数,用key_len计算

explain select * from student where name = '张三';

只使用了name字段进行查询,类型为varchar(24),COLLATE utf8mb4_bin,根据上一篇文章EXplain详解的key_len计算规则可以计算(n*4+2即24*4+2=98),即当前SQL就是使用了name一个字段的索引。 

explain select * from student where name = '张三' and age = '8';

使用了name+age字段进行查询,计算长度(n*4+2+4即24*4+2+4=102),即当前SQL使用了name+age索引 

explain select * from student where name = '张三' and age = '8' and class='一年一班';

使用了name+age+class,长度为(n*4+2+4+n*4+2即24*4+2+4+20*4+2=184),即当前SQL使用了name+age+class索引 

explain select * from student where name = '张三' and class='一年一班' and age = '8';

 将name/age/class的顺序打乱再次执行长度依然是184,说明走了name+age+class索引,原因是MySQL底层会对条件按照索引顺序进行优化。

优化建议:条件按照索引顺序写,不要让mysql在进行排序,减少mysql的负担。

2.最左前缀法则:查询从索引最左前列开始,并且不跳过索引中的列。

explain select * from student where name = '张三' and class='一年一班';

SQL使用name+class字段进行查询,计算两个字段长度(n*4+2+4+n*4+2+4即24*4+2+20*4+2=180)与执行计划中长度98不一致,98只是name的长度,所以可以看出,如果联合索引跳过某一字段,只有此字段之前的索引会生效。

explain select * from student where age = '8' and class='一年一班';

 SQL不包含name,则不走任何索引,进行全表扫描。

3.索引列不使用任何操作(计算、函数、类型转换),否则索引失效查询变成权标扫描

explain select * from student where name = '张三' and age+3 = '11';

 key_len长度是98,只有name字段走了索引,age字段索引失效了。

-- 增加in_date为普通索引
alter table student add index idx_in_date (in_date) using btree ;
explain select * from student where date(in_date) = '2022-09-19';

 查询时对in_date字段进行date类型转换,索引失效,我们可以优化一下使用范围查找,可能会使用到索引。

 在执行计划中给出possible_key:idx_in_date,说明mysql执行的时候可能会使用到这个索引,这个SQL为什么没有使用:mysql内部优化器会根据检索比例、表大小等多个因素整体评估是否使用索引。当前表数据量少,做一次全表扫描也用不了太长时间,而且使用二级索引查找全字段(select *),并没有使用覆盖索引,会进行回表查询,mysql认为这个效率比全表扫描一次效率低,所以用全表扫描。

-- 删除in_date字段索引,还原student表原有索引
alter table student drop index idx_in_date;

4.存储引擎不能使用范围条件右边的列

explain select * from student where name = '张三' and age = '8' and class='一年一班';
explain select * from student where name = '张三' and age > '8' and class='一年一班';

 第一条key_len=184,使用name+age+class索引;第二条key_len=102,使用name+age索引。

 5.尽量使用覆盖索引(索引列包含查询列),减少select * 语句

使用的索引都一样,但是覆盖索引会少一次回表操作。

6.MySQL在使用不等于的时候可能会导致索引会失效,进行全表扫描 

 同3的第二种情形,mysql在执行前会计算是否走索引,如果全表扫描比走索引有效,就会进行全表扫描。

7.is null不会走索引,is not null可能会走索引

8.like以通配符开头,mysql索引会失效,查询变成全表扫描

以通配符开头

通配符不在开头 

 解决like '%字符串%' 索引不被使用方法:

① 使用覆盖索引,查询字段必须是建立联合索引的字段

 ② 如果不能使用覆盖索引,可以借助搜索引擎。

like 'AA%'相当于=常量,like '%AA%' 相当于范围查询

9.字符串不加单引号导致索引失效

10.少用or或者in,用它们查询时mysql不一定使用索引

 11.范围查找优化

-- 增加age字段索引
alter table student add index idx_age (age) using btree ;

这种情况是因为单次数据量查询比较大,导致mysql优化器最终选择不走索引。

优化方法:可以将大范围拆成多个小范围。

  总结:

index(name,age,class)
where语句

索引

索引字段
where name = '张三'Yname
where name = '张三' and age =8Yname,age
where name = '张三' and age =8 and class = '0101'Yname,age,class

where age =8 and class = '0101'

where age =8

where class = '0101'

N

where name = '张三'  and class = '0101'Y

name

(class不能使用索引,age中间断了,class对于name是无序的)

where name = '张三' and age > 8 and class = '0101'Y

name,age

(class不能使用索引,age是范围查询,class在age范围后也是无序的)

where name = '张三' and age like '8%' and class = '0101'Yname,age,class
where name = '张三' and age like '%8' and class = '0101'Yname
where name = '张三' and age like '%8%' and class = '0101'Yname
where name = '张三' and age like '8%8%' and class = '0101'Yname,age,class

like AA%相当于=常量,%AA和%AA%相当于范围查询

索引下推:

5.6版本之前

1.使用二级联合索引最左前缀原则,先找到 like 'name%' 所对应的主键值;

2.使用主键值依次回表到主键索引树中,查询主键值对应的数据行;

3.再进行age和position值对比,得到想要的数据行。

5.6及之后版本

1.使用二级联合索引最左前缀原则,先找到 like 'name%' 所对应的主键值,索引下推去对比age和position的值,筛选出想要数据的主键值;

2.使用筛选后的主键值,回表查询,得到想要的数据行。

 总结:

1.使用了索引下推,在二级索引中获得了有效的主键值,减少了回表次数,提升了SQL查询效率。

2.二级索引使用索引下推,效果明显,主键索引使用索引下推意义不大。

主键索引(聚簇索引)树叶子节点上保存的是全行数据,所以这个时候索引下推并不会起到减少查询全行数据的效果

强制走索引:force index(idx_name_age_class)

EXPLAIN SELECT * FROM student force index(idx_name_age_class) WHERE name > '张三' AND age = 22 AND class ='0101';

强制走索引的效率有时不一定会比扫描全表高,所以mysql会出现建议使用索引,实际上没有走索引的情况。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Seventeen117

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值