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

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 深聊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 by和group 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 字段排序的执行计划如下:

  • 1

执行结果如下:

可以在 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_data 和 sort_buffer_size

回顾一下,是不是很简单,也很Nice。

嗯,细细品,慢慢品!

接下来一段时间,小鱼都会分享更多更好玩的MySQL知识。

可要坚持撸SQL哦~

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
目录
相关文章
|
10天前
|
SQL 前端开发 关系型数据库
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
SpringBoot使用mysql查询昨天、今天、过去一周、过去半年、过去一年数据
41 9
|
12天前
|
缓存 监控 关系型数据库
如何优化MySQL查询速度?
如何优化MySQL查询速度?【10月更文挑战第31天】
39 3
|
17天前
|
SQL NoSQL 关系型数据库
2024Mysql And Redis基础与进阶操作系列(5)作者——LJS[含MySQL DQL基本查询:select;简单、排序、分组、聚合、分组、分页等详解步骤及常见报错问题所对应的解决方法]
MySQL DQL基本查询:select;简单、排序、分组、聚合、分组、分页、INSERT INTO SELECT / FROM查询结合精例等详解步骤及常见报错问题所对应的解决方法
|
16天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
82 1
|
22天前
|
SQL Java 关系型数据库
java连接mysql查询数据(基础版,无框架)
【10月更文挑战第12天】该示例展示了如何使用Java通过JDBC连接MySQL数据库并查询数据。首先在项目中引入`mysql-connector-java`依赖,然后通过`JdbcUtil`类中的`main`方法实现数据库连接、执行SQL查询及结果处理,最后关闭相关资源。
|
18天前
|
SQL 关系型数据库 MySQL
定时任务频繁插入数据导致锁表问题 -> 查询mysql进程
定时任务频繁插入数据导致锁表问题 -> 查询mysql进程
38 1
|
16天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第26天】数据库作为现代应用系统的核心组件,其性能优化至关重要。本文主要探讨MySQL的索引策略与查询性能调优。通过合理创建索引(如B-Tree、复合索引)和优化查询语句(如使用EXPLAIN、优化分页查询),可以显著提升数据库的响应速度和稳定性。实践中还需定期审查慢查询日志,持续优化性能。
47 0
|
18天前
|
SQL 关系型数据库 MySQL
mysql编写sql脚本:要求表没有主键,但是想查询没有相同值的时候才进行插入
mysql编写sql脚本:要求表没有主键,但是想查询没有相同值的时候才进行插入
30 0
|
1月前
|
存储 SQL 关系型数据库
MySQL 给查询结果增列并自定义列数据
MySQL 给查询结果增列并自定义列数据
421 2
|
1月前
|
存储 SQL 关系型数据库
MySQL查询数据库锁表的SQL语句
MySQL查询数据库锁表的SQL语句
82 1