通过两天时间,结合学习视频及自我感悟,自己通过MindManager制作了一个学习笔记,以下是
自己对索引优化的一些些小感悟:
在索引优化之前我们需要先了解索引失效的一些场景,所以我罗列了以下几点,还请各位老师指正,非常感谢!
1.未全值匹配
假设我们先创建了索引index(a),where子句中包含了a和b两个查询条件,表中也无其他索引的情况下
此时查询where子句,我们会发现优化器会选择使用唯一的index(a)
但当我们再创建了另一个索引index(a,b)后,我们再度对同样的where子句进行查询时,我们会发现,优化器选择了index(a,b),而忽略了index(a),使其失效
那是因为优化器发现where中的两个条件刚好与index(a,b)中的a,b全值匹配,那么从查询速度及效率上来说,优化器一定会优先选择全值匹配的这个索引
2.未匹配最佳左前缀法则
这条需要从索引作用的字段列数分析:
①在单列索引中,因为只有一个字段,故没有所谓的最佳左前缀一说,让索引失效的唯一原因就是查询的字段不是我们建立索引的字段;
②在联合索引中 ,设立index(a,b,c),那么在使用索引的过程中,如果没有满足首先使用的索引是条件a,那么不论接下来满足什么条件,该index(a,b,c)就不会生效
3.主键插入导致索引失效
主键自带主键索引,也称聚簇索引。
假设我们有一页完整的数据(上限值为5),主键值为1,3,5,7,9,此时我们手动插入一条新数据,并将主键值手动设定为8,如此一来,原来完整的一页数据因为主键值8的新数据插入,必须分裂成两个页,即页分裂,主键值为9的数据被迫分到第二页中,即数据迁移,这会造成大量的性能损耗
所以我们一般在设定主键时需同步设立auto_increment,使其自递增,防止手动插入主键,造成页分裂。
4.计算导致索引失效
假设我们在number字段上建立了索引,当我们使用where子句查询number = x时,索引是生效的,但当我们将条件改为number+1 = x,多了需要去计算number的值是x-1这一步时,索引就会失效;
?
5.函数导致索引失效
假设我们在字段name上建立索引,当我们使用where子句查询where name = ‘xx’时,索引是生效的,但当我们将name套进函数中,如LEFT(name,2)= ‘xx’时,索引失效
6.类型转换导致索引失效
假设字段上已建立索引,数据类型为varchar,当我们使用where语句where name = 'Jack'时,索引是生效的,但当我们使用where name = Jack时,索引失效
那是因为数据类型varchar需要引号(一时忘了叫啥符来着)对其进行包裹,如果不使用引号,Jack这个字符就会自动转换变成另一个字符以符合条件
7.范围查询未在最右侧导致索引失效
顾名思义,范围查询未在最右侧是指:
假设在建立了联合索引的字段index(a,b,c)中,如果条件a使用了范围查询(>,<等),虽然索引依旧会生效,但索引不会继续深入至b和c字段,它会直接停止并返回仅满足了条件a的那部分数据,同理,如果是条件b使用了,a没使用,那么它会返回满足a和b条件的数据,而忽略了条件c
故我们在创建索引时,需将有范围查询需求的字段,放在最右侧,以保证索引不会失效
8.不等于导致索引失效
从反向进行推导,where子句中的等于(=)查询俗称为等值查询,它的原理就是索引通过这等于的1个值在叶子节点所在页面查找满足这个条件的数据。
但当条件为不等于(!= / <>)时,索引无法精确定位至满足该条件的值,条件不明确就会造成查询混乱,进而导致索引失效
9.通配符位置不当导致索引失效
什么是通配符的位置不当呢?
先对通配符%进行回顾,通配符是代表模糊查询的符号:
① a%的含义是模糊匹配以a为开头的数据;
② %a的含意是模糊匹配以a为结尾的数据;
③ %a%的含义是模糊匹配包含了a的数据;
所以通过索引的原理我们可以得出,方案②③一开始就无法通过索引去进行查询,因为它们的开头都有%,都代表模糊不清,故导致索引失效
因此我们如果需要使用模糊查询,且需要使用模糊查询字段上的索引,那么我们建议只能使用a%这个形式
10.is not null导致索引失效
索引失效原理约等同于【不等于导致索引失效】
当我们在使用where子句查询条件为not null时,索引失效,这是因为在没有指定准确的条件时,表中的任意数据都可被看做为not null
11.or前后存在非索引列导致索引失效
假设我们创建索引index(a),index(b),无索引列c,那么,当我们在where语句中查询a = x or b = y时,索引生效,若查询条件为a = x or c = y时,索引失效
这是因为or代表着或,即满足两个条件中的任一一个条件,该查询即可成立,故优化器在执行条件时发现,即使一开始使用索引找到了a对应的x值,但之后查询c的值时依旧需要去做全表遍历,故不如一开始就对全表进行遍历,还能节省通过索引查找a对应的x值所花的时间
那么,通过以上几点索引会失效的场景,我们可以在现实操作中反其道而行之,达到避坑的效果,哈哈
接下来是部分的优化方式,分为物理查询优化以及逻辑查询优化
物理查询优化:物理查询优化是指通过新建索引或表连接的方式对当前查询条件进行更新,确保查找更有效率的一种方式;
逻辑查询优化:逻辑查询优化是指在原有的索引或查询语句上,对索引逻辑或查询语句的逻辑上进行的一种改良优化,典型的如修改查询语句的结构,改变索引的条件等;
物理查询优化可分为:
1.关联优化
关联分为LEFT/RIGHT JOIN,INNER JOIN
① 不论是left/right/inner join在on 的表字段上添加索引,就可以使type由all优化为ref;
② 保持添加索引的表字段的数据类型一致;
③ 始终遵循小表驱动大表这一原理,因为小表在外层的话可以减少数据回表的次数,且大表内的数据在内层更容易进行优化查询及处理;
④ 多表关联尽量不要超过3张
2.排序优化
除在对where子句上的字段创建索引外,如果我们有排序的需求,也同样建议在ORDER BY的字段上创建索引,这是为什么呢?
因为在语句执行过程中,我们利用where子句上的索引快速定位到我们需要的数据后,接下来数据的呈现方式是由filesort这个默认方式来进行排序的,那么filesort的这种排序方式是在内存中需要cpu参与的,并且在磁盘上I/O排列数据,如果数据量较大会大大降低我们的查询效率;
如果我们在filesort之前使用了索引对数据进行排序,那么我们就不必到内存中,通过cpu再在磁盘上进行I/O并排列,因为索引会将这些通过where过滤的数据,按照我们创建索引的字段的那个方式升序或降序排列
3.分组优化
分组优化的原理与排序优化一致,但如果已经使用了分组优化,建议不再使用排序优化,因为在执行过程中,分组先于排序执行,且分组后排序也已生成,此时再使用排序优化会占用cpu资源,没有太大必要
4.分页优化
分页优化即为在大数据集的情形下,比如我们要查询表中包含a,b,c三个字段的第20000条至20010条的数据,那么此时如果我们直接用
select a,b,c from xxx ORDER BY id ?LIMIT (20000,10),那么数据就会从1遍历到20000,再到20010,结束,这样做效率太低;
所以我们可以通过主键id的形式,将这10条数据筛选出来,即
select id from xxx ORDER BY id LIMIT (20000,10),因为有主键索引在,所以能很快定位到这10条数据
接着我们再将这10条带有主键id的数据join到表中,即
select a,b,c?
from xxx ?x
?join?
(select id?
from xxx
?ORDER BY id?
LIMIT (20000,10)) n
on x.id = n.id;
这样就可优化查询效率
?
5.索引下推
索引下推的含义:
为where子句的字段设立合理的索引,争取通过where子句过滤掉尽可能多的不符合筛选条件的数据,形成一个如下的过滤逻辑
查询字段a,b,c,其中a,b设有索引,那么在where子句会根据最左前缀原则,通过索引a过滤a的数据,再下推至b索引,过滤b数据,因为要查询abc三个字段,故最后发现没有可以下推的字段,遂回表,这样的一点一点将已有索引往下推进行数据过滤的过程称为索引下推;如果字段c也有索引,那么这个过程就是覆盖索引了;
逻辑查询优化建议:
①建议将子查询变为连接查询
从现实查询的角度出发,子查询是建立在临时表上的一种查询方式,它会消耗cpu的资源,且临时表上无法使用索引,在查询结束后,临时表又会被撤销
②建议优先考虑覆盖索引
覆盖索引的含义为在最外层select...from中的字段(就是我们最终想要看到的字段)包含于内层或是被驱动表中我们所查询的字段中,因为这样我们查出来的数据就不需要再回表查询;
举个栗子:
假设a是主键,b是有索引的某个字段,那么
select a,b from table ?where b ?= xx
此时查询出来的数据就不会回表,因为首先主键是默认在叶子节点页中的,其次索引在对应的叶子节点页中发现了要查询的b的值后,发现要查询的a也在,那么此时就不用再回表查询了,即也可以理解为覆盖(回表操作)索引。
因为索引优化的知识点个人觉得有点散,所以以上内容肯定有某些地方出现遗漏,还请各位老师能够指点一下我,感激不尽!