MySQL高级知识(七)——索引优化准则
此博客的内容主要来源于尚硅谷的视频中,在此记录,以备以后自己查看。
准备
创建staff表:
DROP TABLE IF EXISTS `staffs`;
CREATE TABLE `staffs`(
id INT PRIMARY KEY AUTO_INCREMENT,
staname VARCHAR (24) NULL DEFAULT '' COMMENT '姓名',
age INT NOT NULL DEFAULT 0 COMMENT '年龄',
email varchar(20) NULL DEFAULT '' COMMENT '邮箱',
pos VARCHAR (20) NOT NULL DEFAULT '' COMMENT '职位',
add_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间'
) CHARSET utf8 COMMENT '员工记录表' ;
INSERT INTO `staffs`(staname ,age,pos,add_time) VALUES('z3',22,'manager',NOW());
INSERT INTO `staffs`(staname ,age,pos,add_time) VALUES('July',23,'dev',NOW());
INSERT INTO `staffs`(staname ,age,pos,add_time) VALUES('2000',23,'dev',NOW());
INSERT INTO `staffs`(staname ,age,pos,add_time) VALUES(null,23,'dev',NOW());
SELECT * FROM staffs;
规则:
1. 最佳左前缀原则
1.1. 定义:
- 在创建了多列索引的情况下,查询从索引的最左前端列开始且不能跳过索引中的列。
- 最佳左前缀法则就是说,如果我们创建了多个索引,在使用索引的时候要按照创建索引的顺序来使用,中间不能缺少或者跳过,当然如果知识使用了最左边的索引列,也就是第一个索引是可以的。
- 俗话理解就是"带头大哥不能死,中间兄弟不能断"。
1.2. 事例
- 事例1
创建组合索引,并执行explain
分析:
- 索引的创建顺序是staname,age,pos;
- 直接使用staname(带头大哥)作为条件,可以看到type=ref,key_len=75,ref=const,效果不错
- 事例2
分析:
没使用带头大哥(staname),直接使用兄弟,type=ALL,为全表扫描。
- 事例3
分析:
使用了带头大哥(staname),之后使用了age,所以type=ref,ref=const,const。
注意:
and 忽略左右关系。就是我们即使没有按照顺序,但是mysql由于优化器的存在,会自动优化这个。
- 事例4
分析:
-
对比上面两个sql语句可发现:火车头(staname)和中间车厢(age)、火车头(staname)和车尾(pos)。
-
虽然type=ref,但是观察key_len和ref两项,并对比Case1中的结果,可得出在使用火车头(name)和车尾(gender)时,只使用了部分索引也就是火车头(name)的索引。
-
通俗理解:火车头单独跑没问题,火车头与直接相连的车厢一起跑也没问题,但是火车头与车尾,如果中间没有车厢,只能火车头自己跑。
- 事例5
分析:
火车头 + 车厢 + 车尾,三者串联,就变成了整个火车。type=ref,key_len=141,ref=const,const,const。此处的and左右顺序关系可以忽略,mysql优化器会自动优化。
1.3. 总结
带头大哥不能死,中间兄弟不能断;带头大哥可跑路,老二也可跟着跑,其余兄弟只能死。
2. 不要在索引列上面做任何的操作
在索引列上面做任何的操作(计算、函数、(自动or手动)类型转换),会导致索引失效从而转向全表扫描。
- 事例1
分析:
- 这里使用了函数计算,type=ALL,导致索引失效。
- 事例2
分析:
-
将name=‘Tom’的值修改为‘123’,使用sql后,发生了类型转换,type=ALL,导致全表扫描。
-
如果条件为where staname = ‘123’,那么没有发生类型转换。
在索引列上做任何操作,都会导致索引失效转向全表扫描。
3. 范围右边全失效
存储引擎不能使用索引中范围右边的列,也就是说范围右边的索引列会失效。
- 事例1
- 事物2
- 事物3
- 事物4
- 对比:
-
条件单独使用name时候,type=ref,key_len=75,ref=const。
-
条件加上age时(使用常量等值),type=ref,key_len=79,ref=const,const。
-
当全值匹配时,type=ref,key_len=141,ref=const,const,const。说明索引全部用上,从key_len和ref可以看出。(and忽略左右)
-
当使用范围时(age>22),type=79,key_len=79,ref=Null,与事例1、事例2和事例3可以,使用了部分索引,但是pos这个字段的索引并没有使用上。
-
范围右边的索引列失效。
4. 尽量使用覆盖索引
尽量使用覆盖索引(查询列和索引列尽量保持一直,通俗的讲就是对A、B列创建了索引,然后查询中尽量也使用A、B列),减少select * 的使用。
- 事例1
- 事例2
- 分析:
- 对比事例1和事例2,事例1使用了select * ,事例2使用了覆盖索引(查询列和条件列对应),可看到extra从Null变成了Using index,提高了检索效率。
5. 使用不等于( !=或<>)会使索引失效
结论:使用!=会使type=ALL,key=Null,导致全表扫描,并且索引失效。
6. is not null也无法使用索引
结论:使用is not null的时候,type=ALL全表扫描,key=Null索引失效。
此处type=ref,key=idx_name_age_pos,表明可以使用索引的。
注意:is null是可以使用索引的。
7. like通配符以%开头会使索引失效
- 事例1
- 事例2
- 事例3
- 分析:
-
like的%位置不同,所产生的效果不一样,当%出现在左边的时候,type=ALL,key=Null(全表扫描,索引失效),当%出现在右边的时候,type=range,索引未失效。
-
like查询为范围查询,%出现在左边,则索引失效。%出现在右边索引未失效。口诀:like百分加右边。
-
但是在实际生产环境中,%仅出现在右边可能不能够解决我们的问题,所以解决%出现在左边索引失效的方法:使用覆盖索引。
- 事例4
分析:对比事例1可知,通过覆盖所有type=index,并使用了Using index,从全表扫描变成了全所以扫描,还是不错的。
- 事例5
分析:这里出现type=index,是因为主键自动创建唯一索引。
- 事例6
explain select age from staffs where staname like '%Jack%';
explain select staname,age from staffs where staname like '%Jack%';
explain select staname,age,pos from staffs where staname like '%Jack%';
explain select id,staname,age,pos from staffs where staname like '%Jack%';
---
explain select pos from staffs where staname like '%Jack%'; #结果也一样
分析:上面四组explain执行的结果都相同,表明都使用了索引,从这里可以深刻的体会到覆盖所有:完全吻合或者沾边(age、pos),都可以使得type=index。
- 事例7
分析:由于只在(staname、age、pos)上面创建索引,当包含email时候,导致结果集偏大(email未建立索引)【锅大,锅盖小,不能匹配】,所以type=ALL。
8. 字符串不加单引号导致索引失效
分析:
-
上述两条sql语句实际操作得到的数据是相同的。
-
通过explain执行结果可以看出,字符串(staname)不加单引号在查询的时候,导致索引失效(type=ref准变成了type=ALL,并且key=Null),并且全表扫描。
varchar类型的字段,在查询的时候不加单引号导致索引失效,转向全表扫描。
因为他可能会将这个转变类型。
9. 少用or,用or连接会使索引失效
分析:通过上述explain的执行结果可以看出,在执行or连接的时候,type=ALL,key=Null,导致索引失效,并全表扫描。
总结:
-
全值匹配我最爱。
-
最佳左前缀法则: 带头大哥不能死,中间兄弟不能断;带头大哥可跑路,老二也可跟着跑,其余兄弟只能死。
-
索引列上不计算。
-
覆盖索引记住用。
-
不等于、is not null导致索引失效。
-
like百分加右边,加左边导致索引失效,解决方法:使用覆盖索引。
-
字符串不加单引号导致索引失效。
-
少用or,用or导致索引失效。