深聊MySQL,从入门到入坟之:如何让order by、group by查询速度飞起来

1、引言

在结束性能知识分享,我们终于迎来了MySQL的 知识。
关于,MySQL的基本用法,小鱼也是整理的几篇文章,如:
基本用法总括一
基本用法总括二
SQL去重查询数据
SQL语句多个表查询,inner join的用法
sql数据库中的 delete 与drop的区别

接下来,小鱼会更详细的分享关于 MySQL的知识,包含这四大类:
SQL优化
MySQL索引
MySQL锁
事务

接下来的很长一段时间,
就让小鱼陪着你,
一起把 MySQL的姿()势() 搞起来!!!

1.1 MySQL的基本知识

按照惯例,先介绍一下 MySQL基本知识:

MySQL 是一种关联数据库管理系统,关联数据库将数据保存在不同的表中,而不是将所有数据放在一个大仓库内,这样就增加了速度并提高了灵活性。
MySQL 的 SQL “结构化查询语言”,是用于访问数据库的最常用标准化语言。
MySQL 软件采用了 GPL(GNU 通用公共许可证),由于其体积小、速度快、总体拥有成本低,尤其是开放源码这一特点,许多中小型网站为了降低网站总体拥有成本,而选择 MySQL 作为其网站数据库。

1.2 MySQL的优势

MySQL的优势有哪些,我们来瞧瞧。

MySQL开源的,无需支付额外费用的

MySQL使用标准的SQL数据语言形式

MySQL可以运行多个系统上,并支持多个语言(如:C、C++、Python、Java、Perl、PHP、Eiffel、Ruby 等);

MySQL可以定制,采用 GPL 协议,可修改源码来开发自己的 MySQL 系统

所以,我们要搞好MySQL数据库,是不是非常有必要。

今天,
我们就先从 order bygroup by来说起。

2、order by 原理

为了更好,更容易理解相关知识点,咱先举个例子。

也是小鱼的一贯作风, 举例说话 !

先创建一张测试表

/* auth:carl_DJ  */

/* 使用ClassDJ这个database */
use ClassDJ;  

/* 如果表t1存在则删除表t1 */
drop table if exists t1;  

/* 创建表t1 */
CREATE TABLE 't1'(      
	'id' int(11) NOT NULL AUTO_INCREMENT,
	'a' int(20) DEFAULT NULL ,
	'b' int(20) DEFAULT NULL ,
	'c' int(20) DEFAULT NULL ,
	'd' datetime NOT NULL  DEFAULT CURRENT_TIMESTAMP ,
	PRIMARY KEY (`id`),
	KEY `idx_a_b` (`a`,`b`),
	KEY `idx_c` (`c`)

)	ENGINE=InnoDB CHARSET=utf8mb4 ;


/* 如果存在存储过程insert_t1,则删除 */
drop procedure if exists insert_t1;
delimiter ;;

/* 创建存储过程insert_t1 */
create procedure insert_t1()
begin 
  /* 声明变量i */
  declare i int;  
  /* 设置i的初始值为1 */
  set i=1;  		
  /* 对满足i<=10000的值进行while循环 */
  while(i<=10000)do
  	/* 写入表d1中a、b两个字段,值都为i当前的值 */
    insert into t1(a,b,c) values(i,i,i); 
    /* 将i加1 */
    set i=i+1;    
  end while;
end ;;
delimiter ;
 /* 运行存储过程insert_t1 */
call insert_t1();       

/* 将id大于9000的行的a字段更新为1000 */
update t1 set a=1000 where id >9000;



创建完表,我们就来研究一下MySQL的排序原理

2.1 MySQL的排序方式

按照排序原理分,MySQL 排序方式分两种

通过有序索引直接返回有序数据

通过Filesort进行的排序

那么,问题来了,

如何确定某条排序的SQL所使用的排序方式

:使用explain来查看该排序SQL的执行计划,重点关注****字段。

★如果Extra字段里显示是 Using index,则表示是通过有序索引直接返回有序数据

例如

explain select id,c from t1 order by c;

在这里插入图片描述

★如果Extra字段里显示是 Using filesort,则表示该 SQL 是通过 Filesort 进行的排序

例如

explain select id,d from t1 order by d;

在这里插入图片描述
是不是,这就一目了然 了。
Nice

2.2 Filesort 在哪里完成排序

MySQL 中的 Filesort 并不一定是在磁盘文件中进行排序的,也有可能在内存中排序,
内存排序还是磁盘排序取决于排序的数据大小sort_buffer_size 配置的大小

・如果 “排序的数据大小” < sort_buffer_size: 内存排序

・如果 “排序的数据大小” > sort_buffer_size: 磁盘排序

同样,问了又来了,

如何确定使用 Filesort 排序的 SQL 是在内存还是在磁盘中进行的排序操作

:使用 trace 进行分析重点关注 number_of_tmp_files

★如果等于 0,则表示排序过程没使用临时文件,在内存中就能完成排序;

★如果大于0,则表示排序过程中使用了临时文件

举例

一、未使用临时文件排序

在这里插入图片描述

上图中,因为 number_of_tmp_files 等于 0,表示未使用临时文件进行排序,所以是内存排序

参数解析:

・rows:预计扫描的行数
・examined_rows:参与排序的行
・number_of_tmp_files:使用临时文件的个数
・sort_buffer_size:sort_buffer 的大小
・sort_mode:排序模式

二、使用临时文件排序

在这里插入图片描述

上图中,因为 number_of_tmp_files 等于 7,所以表示使用的是磁盘排序

number_of_tmp_files 等于 7表示

该SQL 将需要排序的数据分为 7 份,然后每份单独排序,再存放在 7 个临时文件中,最后把 7 个临时文件合并成一个大的有序文件。

2.3 Filesort 下的排序模式

排序模式
Filesort 下的排序模式有三种

< sort_key, rowid >双路排序(又叫回表排序模式):是首先根据相应的条件取出相应的排序字段和可以直接定位行数据的行 ID,然后在 sort buffer 中进行排序,排序完后需要再次取回其它需要的字段;

< sort_key, additional_fields >单路排序:是一次性取出满足条件行的所有字段,然后在sort buffer中进行排序;

< sort_key, packed_additional_fields >打包数据排序模式:与单路排序相似,区别是将 char 和 varchar 字段存到 sort buffer 中时,更加紧缩。

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

判断排序模式
通过 max_length_for_sort_data 的大小和需要查询的字段总大小来判断使用哪种排序模式。

・如果 max_length_for_sort_data 比查询字段的总长度大,那么使用 < **sort_key, additional_fields >**排序模式;

・如果 max_length_for_sort_data 比查询字段的总长度小,那么使用 <sort_key, rowid> 排序模式。

实例举例


set session optimizer_trace="enabled=on",end_markers_in_json=on;

SET max_length_for_sort_data = 20;

/* 查询表d1的id、a、d三个字段的值,按照字段d进行排序  */
select a,d from t1 order by d; 

SELECT * FROM information_schema.OPTIMIZER_TRACE\G

OPTIMIZER_TRACE 结果中排序信息,如下图:

在这里插入图片描述
可以看到,使用的是**<sort_key,additional_fields>**排序模式。


我们要是变成 <sort_key, rowid>,怎么整呢?

思路:因为 a、d 两个字段的总长度为 12,可以尝试把 max_length_for_sort_data 改为小于 12 的值,看排序模式是否有改变。

操作


set session optimizer_trace="enabled=on",end_markers_in_json=on;

SET max_length_for_sort_data = 4;

/* 查询表d1的id、a、d三个字段的值,按照字段d进行排序  */
select a,d from t1 order by d; 

SELECT * FROM information_schema.OPTIMIZER_TRACE\G

OPTIMIZER_TRACE 结果中排序信息,如下图:

在这里插入图片描述

看看,是不是变成 <sort_key ,rowid>排序模式了。
非常Nice。

3、order by 优化

我们了解了order by的原理,
那么我们就来看看,优化order by 有什么技巧。

3.1 添加合适索引

3.1.1 排序字段添加索引

①首先我们看下对 d 字段(没有索引)进行排序的执行计划:

explain select d,id from t1 order by d;

执行结果如下:

在这里插入图片描述
发现使用的是 filesort(关注 Extra 字段)。

②再看些对 c 字段(有索引)进行排序的执行计划:

explain select c,id from t1 order by c;

执行结果如下:
在这里插入图片描述
可以看到,根据有索引的字段排序,在 Extra 中显示的就为 Using index,表示使用的是索引排序

如果数据量比较大,显然通过有序索引直接返回有序数据效率更高

3.1.2 多个字段排序优化

:有时面对的需求是要对多个字段进行排序,而这种情况应该怎么优化或者设计索引呢?

如果多个字段排序,可以在多个排序字段上添加联合索引来优化排序语句

举个例子

对 a、c 两个字段进行排序的执行计划

explain select id,a,c from t1 order by a,c;

执行结果如下:
在这里插入图片描述

观察 Extra 字段,发现使用的是 filesort


再看对 a、b(a、b 两个字段有联合索引)两个字段进行排序。

explain select id,a,b from t1 order by a,b;

执行结果如下:
在这里插入图片描述
发现使用的是索引排序

多个字段排序的情况,如果要通过添加索引优化,得注意排序字段的顺序与联合索引中列的顺序要一致

3.1.3 先等值查询再排序的优化

:我们更多的情况是会先根据某个字段条件查出一部分数据,然后再排序,而这类 SQL 应该如果优化呢?

可以通过在条件字段和排序字段添加联合索引来优化此类排序语句。

举个例子


表 d1中,根据 a=1000 过滤数据再根据 d 字段排序的执行计划如下:

explain select id,a,d from t1 where a=1000 order by d;

执行结果如下:
在这里插入图片描述
可以在 Extra 字段中看到 “Using filesort”,说明使用的是 filesort 排序


再看下根据 a=1000 过滤数据在根据 b 字段排序的执行计划(a、b 两个字段有联合索引):

explain select id,a,b from t1 where a=1000 order by b;

执行结果如下:
在这里插入图片描述

可以在 Extra 字段中看到“Using index”,说明使用的是索引排序

3.2 去掉不必要的返回字段

有时,我们其实并不需要查询出所有字段,但是,有的时候,我们习惯性的写出 “select * from table_name”。

举个例子

select * from t1 order by a,b;           /* 根据a和b字段排序查出所有字段的值 */

执行结果如下:
在这里插入图片描述


select id,a,b from t1 order by a,b;      /* 根据a和b字段排序查出id,a,b字段的值 */

执行结果如下:
在这里插入图片描述

通过这两个例子,我们可以看到,
查询所有字段的这条 SQL 是 filesort 排序
而只查 id、a、b 三个字段的 SQL 是 index 排序

那么,

为什么查询所有字段会不走索引?

是因为:

扫描整个索引并查找到没索引的行的成本比扫描全表的成本更高,所以优化器放弃使用索引

3.3 修改参数

一、修改哪些参数
这里就用到了在前面提到的两个参数:

max_length_for_sort_data
sort_buffer_size。

二、如何修改这两个参数的值

max_length_for_sort_data
如果觉得排序效率比较低,可以适当加大 max_length_for_sort_data 的值,让优化器优先选择全字段排序。当然不能设置过大,可能会导致 CPU 利用率过低或者磁盘 I/O 过高;

sort_buffer_size
适当加大 sort_buffer_size 的值,尽可能让排序在内存中完成。但不能设置过大,可能导致数据库服务器 SWAP。

4、 group by 优化

默认情况,会对 group by 字段排序
因此优化方式与 order by 基本一致
如果目的只是分组而不用排序,可以指定 order by null 禁止排序

5、总结

今天,整理的是 order by 和group by的 一些优化技巧,
因为在写sql中,这两个还是比较常用的,
所以,记住还是有必要的。

关于order by的优化,主要就这么几点知识

通过添加合适索引
去掉不必要的返回字段
调整参数:主要是 max_length_for_sort_datasort_buffer_size

回顾一下,是不是很简单,也很Nice。
嗯,细细品,慢慢品!
接下来一段时间,小鱼都会分享更多更好玩的MySQL知识。
可要坚持撸SQL哦~

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Carl_奕然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值