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 = '张三';

indesign索引怎么做_字段

只使用了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';

indesign索引怎么做_字段_02

使用了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='一年一班';

indesign索引怎么做_数据库_03

使用了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';

indesign索引怎么做_数据库_04

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

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

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

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

indesign索引怎么做_mysql_05

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='一年一班';

indesign索引怎么做_mysql_06

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

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

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

indesign索引怎么做_数据库_07

 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';

indesign索引怎么做_java_08

使用范围查找,可能会使用到索引。

indesign索引怎么做_java_09

 在执行计划中给出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='一年一班';

indesign索引怎么做_字段_10

indesign索引怎么做_mysql_11

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

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

indesign索引怎么做_indesign索引怎么做_12

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

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

indesign索引怎么做_字段_13

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

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

indesign索引怎么做_字段_14

indesign索引怎么做_indesign索引怎么做_15

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

以通配符开头

indesign索引怎么做_字段_16

通配符不在开头 

indesign索引怎么做_indesign索引怎么做_17

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

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

indesign索引怎么做_java_18

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

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

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

indesign索引怎么做_java_19

indesign索引怎么做_java_20

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

indesign索引怎么做_indesign索引怎么做_21

 11.范围查找优化

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

indesign索引怎么做_java_22

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

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

indesign索引怎么做_java_23

  总结:

index(name,age,class)

where语句

索引

索引字段

where name = '张三'

Y

name

where name = '张三' and age =8

Y

name,age

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

Y

name,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'

Y

name,age,class

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

Y

name

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

Y

name

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

Y

name,age,class

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

索引下推:

5.6版本之前

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

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

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

5.6及之后版本

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

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

indesign索引怎么做_indesign索引怎么做_24

 总结:

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会出现建议使用索引,实际上没有走索引的情况。