深入浅出Mysql(三)-Mysql索引优化实战-上篇

70 阅读5分钟

前言

我本人是偏向与实战派系,前两篇的理论结束了,现在开启实战篇!!

跟着本文你将学到:

1、索引下推优化

2、Mysql优化器索引选择探究

3、索引优化Order by与Group by

4、Using filesort文件排序详解

5、索引设计原则与实战

一、准备工作

(1)建表

CREATE TABLE `employees` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(24) NOT NULL DEFAULT '' COMMENT '姓名',
`age` int(11) NOT NULL DEFAULT '0' COMMENT '年龄',
`position` varchar(20) NOT NULL DEFAULT '' COMMENT '职位',
`hire_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '入职时间',
PRIMARY KEY (`id`),
KEY `idx_name_age_position` (`name`,`age`,`position`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COMMENT='员工记录表';

(2)插入数据

INSERT INTO employees(name,age,position,hire_time) VALUES('LiLei',22,'manager',NOW());

INSERT INTO employees(name,age,position,hire_time) VALUES('HanMeimei', 23,'dev',NOW());

INSERT INTO employees(name,age,position,hire_time) VALUES('Lucy',23,'dev',NOW());


drop procedure if exists insert_emp;
delimiter ;;
create procedure insert_emp()
begin
declare i int;
set i=1;
while(i<=100000)do
insert into employees(name,age,position) values(CONCAT('curtis',i),i,'dev');
set i=i+1;
end while;
end;;
delimiter ;
call insert_emp();

二、实战

1、索引下推

EXPLAIN SELECT * FROM employees_copy WHERE name like 'LiLei%' AND age = 22 AND position ='manager';

image.png

对于辅助的联合索引(name,age,position),正常情况按照最左前缀原则,SELECT * FROM employees WHERE name like 'LiLei%'

AND age = 22 AND position ='manager' 这种情况只会走name字段索引,因为根据name字段过滤完,得到的索引行里的age和position是无序的,无法很好的利用索引。

在MySQL5.6之前的版本,这个查询只能在联合索引里匹配到名字是 'LiLei' 开头的索引,然后拿这些索引对应的主键逐个回表,到主键索

引上找出相应的记录,再比对age和position这两个字段的值是否符合。

MySQL 5.6引入了索引下推优化,可以在索引遍历过程中,对索引中包含的所有字段先做判断,过滤掉不符合条件的记录之后再回表,可以有效的减少回表次数。 使用了索引下推优化后,上面那个查询在联合索引里匹配到名字是 'LiLei' 开头的索引之后,同时还会在索引里过滤age和position这两个字段,拿着过滤完剩下的索引对应的主键id再回表查整行数据。

为什么范围查找Mysql没有用索引下推优化?

估计应该是Mysql认为范围查找过滤的结果集过大,like KK% 在绝大多数情况来看,过滤后的结果集比较小,所以这里Mysql选择给 likeKK% 用了索引下推优化,当然这也不是绝对的,有时like KK% 也不一定就会走索引下推。

2、MySQL如何选择合适的索引

EXPLAIN select * from employees where name > 'a';

image.png 如果用name索引需要遍历name字段联合索引树,然后还需要根据遍历出来的主键值去主键索引树里再去查出最终数据,成本比全表扫描

还高,可以用覆盖索引优化,这样只需要遍历name字段的联合索引树就能拿到所有结果

EXPLAIN select * from employees where name > 'zzz' ;

image.png

对于上面这两种 name>'a' 和 name>'zzz' 的执行结果,mysql最终是否选择走索引或者一张表涉及多个索引,mysql最终如何选择索引,可以用trace工具来一查究竟,开启trace工具会影响mysql性能,所以只能临时分析sql使用,用完之后立即关闭。

set session optimizer_trace="enabled=on",end_markers_in_json=on; ‐‐开启trace

select * from employees where name > 'a' order by position;

SELECT * FROM information_schema.OPTIMIZER_TRACE;

3、常见sql优化(Order by与Group by)

(1)Case1:

image.png

分析:

利用最左前缀法则:中间字段不能断,因此查询用到了name索引,从key_len=74也能看出,age索引列用在排序过程中,因为Extra字段里没有using filesor

(2)Case2:

image.png 分析:

从explain的执行结果来看:key_len=74,查询使用了name索引,由于用了position进行排序,跳过了age,出现了Using filesort

(3)Case3:

image.png 分析:

查找只用到索引name,age和position用于排序,无Using filesort。

(4)Case4:

image.png 分析:

和Case 3中explain的执行结果一样,但是出现了Using filesort,因为索引的创建顺序为name,age,position,但是排序的时候age和position颠倒位置了。

(5)Case5:

image.png 分析:

与Case 4对比,在Extra中并未出现Using filesort,因为age为常量,在排序中被优化,所以索引未颠倒,不会出现Using filesort。

(6)Case6:

image.png 分析:

虽然排序的字段列与索引顺序一样,且order by默认升序,这里position desc变成了降序,导致与索引的排序方式不同,从而产生Using filesort。Mysql8以上版本有降序索引可以支持该种查询方式。

(7)Case7:

image.png 分析:

对于排序来说,多个相等条件也是范围查询

(8)Case8:

image.png 可以用覆盖索引优化

image.png

(9)优化总结

  1. MySQL支持两种方式的排序filesort和index,Using index是指MySQL扫描索引本身完成排序。index效率高,filesort效率低。
  2. order by满足两种情况会使用Using index。
  • order by语句使用索引最左前列。

  • 使用where子句与order by子句条件列组合满足索引最左前列。

  1. 尽量在索引列上完成排序,遵循索引建立(索引创建的顺序)时的最左前缀法则。

  2. 如果order by的条件不在索引列上,就会产生Using filesort。

  3. 能用覆盖索引尽量用覆盖索引

  4. group by与order by很类似,其实质是先排序后分组,遵照索引创建顺序的最左前缀法则。对于group by的优化如果不需要排序的可以加上order by null禁止排序。注意,where高于having,能写在where中的限定条件就不要去having限定了。

4、Using filesort文件排序原理详解

(1)单路排序

是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;用trace工具可以看到sort_mode信息里显示< sort_key, additional_fields >或者< sort_key,packed_additional_fields >。

一次全部取出,然后进行排序。

(2)双路排序(又叫回表排序模式)

是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段;用trace工具可以看到sort_mode信息里显示< sort_key, rowid >。

取出排序字段和ID,然后排序,排序好再回表去取对应数据。

(3)排序设置

MySQL 通过比较系统变量 max_length_for_sort_data(默认1024字节) 的大小和需要查询的字段总大小来判断使用哪种排序模式。

  • 字段的总长度小于max_length_for_sort_data ,那么使用 单路排序模式;

  • 字段的总长度大于max_length_for_sort_data ,那么使用 双路排序模式。

4、索引设计原则

(1)代码先行,索引后上

一般应该等到主体业务功能开发完毕,把涉及到该表相关sql都要拿出来分析之后再建立索引。

(2)联合索引尽量覆盖条件

比如可以设计一个或者两三个联合索引(尽量少建单值索引),让每一个联合索引都尽量去包含sql语句里的where、order by、group by的字段,还要确保这些联合索引的字段顺序尽量满足sql查询的最左前缀原则。

(3)不要在小基数字段上建立索引

索引基数是指这个字段在表里总共有多少个不同的值,比如一张表总共100万行记录,其中有个性别字段,其值不是男就是女,那么该字段的基数就是2。

如果对这种小基数字段建立索引的话,还不如全表扫描了,因为你的索引树里就包含男和女两种值,根本没法进行快速的二分查找,那用索引就没有太大的意义了。

一般建立索引,尽量使用那些基数比较大的字段,就是值比较多的字段,那么才能发挥出B+树快速二分查找的优势来。

5、索引设计实战

待更新...