文章目录
- 1. 索引失效的案例
- 1.1 最左优先
- 1.2 主键插入顺序
- 1.3 计算、函数、类型转换(自动或手动)导致索引失效
- 1.4 范围条件右边的列索引失效
- 1.5 非 条件索引失效
- 1.6 like以通配符%开头索引失效
- 1.7 OR 前后存在非索引的列,索引失效
- 2. 关联查询优化
- 3. 子查询优化
- 3.1 子查询缺点
- 4. 排序优化
- 5. GROUP BY优化
- 6. 优化分页查询
- 7. 覆盖索引与索引下推
- 8. 普通索引 vs 唯一索引
- 8.1 查询过程
- 8.2 更新过程
1. 索引失效的案例
1.1 最左优先
1.2 主键插入顺序
数据页和记录又是按照记录主键值从小到大的顺序进行排序,所以如果我们插入的记录的主键值是依次增大的话,那我们每插满一个数据页就换到下一个数据页继续插,而如果我们插入的主键值忽小忽大的话,就比较麻烦了,可能会造成页面分裂
和记录移位
- 让主键具有 AUTO_INCREMENT ,让存储引擎自己为表生成主键,而不是我们手动插入
1.3 计算、函数、类型转换(自动或手动)导致索引失效
第一种:对索引字段作用函数,导致索引失效:
第二种 对索引字段计算,导致索引失效:
第三种:类型转换导致索引失效:
1.4 范围条件右边的列索引失效
(age、classId、name)的顺序创建的聚合索引
原因:因为前一个条件相同的情况下 当前条件才会是有序的。
当前一个条件不同 那么无法保证当前条件为有序的 所以索引失效
其实就是二级索引的话,如果都是等值判断的话,那是有序的,先查age,age选出一部分数据后再查classId,classId再筛选一部分后再查name;如果不是等值查询的话,比如说classId使用到了范围,那经过classId范围筛选出一部分数据后,此时对于classId后面的所有索引来说,此时的状态是无序的,关键字不是有序,只能逐个查找,导致索引失效。
1.5 非 条件索引失效
- 不等于(!= 或者<>)索引失效
- is null可以使用索引,is not null无法使用索引(相当于非条件)
1.6 like以通配符%开头索引失效
1.7 OR 前后存在非索引的列,索引失效
or其实就是取并集,两个条件的前面有索引,后面没有索引,但要取并集,还是要扫描全表,导致索引失效。
使用到了索引:而且是两个索引
你能看到这里使用到了 index_merge,简单来说index_merge
就是对age和name分别
进行了扫描,然后将这两个结果集进行了合并
。这样做的好处就是 避免了全表扫描
。
2. 关联查询优化
- 外连接
- 右表是我们的关键点,一定需要建立索引 。
- 内连接
- 对于内连接来说,查询优化器可以决定谁作为驱动表,谁作为被驱动表出现的
- 小表驱动大表
- 被驱动表创建索引
Join语句的原理:
- Simple Nested-Loop Join(索引嵌套循环连接)外表取一个记录,内表都拿过来判断
- Index Nested-Loop Join(索引嵌套循环连接)外表取一个记录,根据拿到的匹配字段走索引,再将索引查出来的进行匹配
- Block Nested-Loop Join(块嵌套循环连接)驱动表批量加载到join buffer中,外表加载一定的次数,进行批量匹配,这个一定次数是基于驱动表分几次可以加载到join buffer中的
Mysql8之后: Hash Join
3. 子查询优化
3.1 子查询缺点
- 执行子查询时,MySQL需要为内层查询语句的查询结果建立一个
临时表
,然后外层查询语句从临时表中查询记录。查询完毕后,再撤销这些临时表。这样会消耗过多的CPU和IO资源,产生大量的慢查询。 - 子查询的结果集存储的临时表,不论是
内存临时表
还是磁盘临时表
都不会存在索引
,所以查询性能会受到一定的影响。
结论:尽量不要使用NOT IN 或者 NOT EXISTS,用LEFT JOIN xxx ON xx WHERE xx IS NULL替代
4. 排序优化
问题:在 WHERE 条件字段上加索引,但是为什么在 ORDER BY 字段上还要加索引呢?
where先根据索引查到了数据,但如果ORDER BY字段没有加索引的话,查到的这些数据对于ORDER BY的字段来说的话是乱序的,要重新排序后再返回sql语句的结果。
在MySQL中,支持两种排序方式,分别是 FileSort 和 Index 排序。
排序中的索引失效问题:
优化建议:
-
SQL 中,可以在 WHERE 子句和 ORDER BY 子句中使用索引,目的是在
WHERE 子句中 避免全表扫描
,在ORDER BY 子句避免使用 File Sort 排序
。当然,某些情况下全表扫描,或者 File Sort 排序不一定比索引慢。但总的来说,我们还是要避免,以提高查询效率。 -
尽量使用 Index 完成 ORDER BY 排序。如果 WHERE 和 ORDER BY 后面是相同的列就使用单索引列;如果不同就使用联合索引。
-
无法使用 Index 时,需要对 File Sort 方式进行调优。
5. GROUP BY优化
-
group by 使用索引的原则几乎跟order by一致 ,group by 即使没有过滤条件用到索引,也可以直接使用索引。
-
group by
先排序再分组
,遵照索引建的最佳左前缀法则 -
当无法使用索引列,可以增大max_length_for_sort_data和sort_buffer_size参数的设置
-
where效率高于having,能写在where限定的条件就不要写在having中了
-
减少使用order by,和业务沟通能不排序就不排序,或将排序放到程序端去做。Order by、group by、distinct这些语句较为耗费CPU,数据库的CPU资源是极其宝贵的。
-
包含了order by、group by、distinct这些查询的语句,where条件过滤出来的结果集请保持在1000行以内,否则SQL会很慢。
6. 优化分页查询
问题:此时需要MySQL排序前2000010记录,仅仅返回2000000 -2000010的记录,其他记录丢弃,查询排序的代价非常大。
7. 覆盖索引与索引下推
口述:覆盖索引和索引下推其实最初思想其实是差不多的,都是为了避免或者减少这种回表的次数,因为回表嘛,其实就是从二级索引中拿到主键,再回表,查询出详细的数据,回表其实是一个随机的IO,因为我们从二级索引查出的这个主键不是有序的,那回表就可能页在左边或者中间或者右边,是相当分散的,所以要尽量的避免。
索引覆盖其实就是针对我们的二级索引,当我们select字段是聚合索引的一部分,当然主键那也可以,那我们根据where啊这种根据索引找到数据后,按道理来说是要拿到主键再回表的,但我们select字段是聚合索引的一部分,其实二级索引的B+tree中就会存储聚合索引的消息,压根不需要回表,其实这个就是索引覆盖,就是不用回表了。
索引下推的话,其实本质来说还是要减少回表的次数的,其实就是将我们的条件提前到了回表之前,按道理我们用一个索引查出数据后,要回表查详细数据,这里的话其实索引下推针对的是聚合索引,比如a b c三个条件构成聚合索引,先按a索引查到数据,索引下推其实是用在聚合索引后面字段失效的情况下的,因为其实如果不失效的话,那它根本不会走索引下推,之间走聚合索引的ok了,索引下推是聚合索引后面字段索引失效了,将后续条件的判断提前到回表之前,进一步筛选掉一部分数据后再回表,也是减少回表要查的数据量的,因为回表其实是相当于一种随机IO嘛,性能很差,索引下推也是优化器来优化性能的。
ICP的使用条件:
-
如果表访问的类型为range、ref、eq_ref和ref_or_null可以使用ICP
-
ICP可以用于 InnoDB 和 MyISAM 表,包括分区表InnoDB和 MyISAM表
-
对于 InnoDB 表,ICP仅用于 二级索引 。ICP的目标是减少全行读取次数,从而减少I/O操作。
-
当SQL使用覆盖索引时,不支持ICP。因为这种情况下使用ICP不会减少I/O。
-
相关子查询的条件不能使用ICP
8. 普通索引 vs 唯一索引
8.1 查询过程
影响微乎其微
8.2 更新过程
- 其实,这两类索引在查询能力上是没差别的,主要考虑的是对 更新性能 的影响。所以,建议你
尽量选择普通索引
- 普通索引 可以使用change buffer ,对于 数据量大 的表的更新优化还是很明显的。唯一索引不能使用
change buffer其实就是:当更新时,先写入change buffer,不同步磁盘,当下一次这个页从磁盘读入内存时,对这个页执行change buffer修改的操作,实现数据的一致性。