小胖问我:group by 怎么优化?(下)

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
云数据库 RDS MySQL,高可用系列 2核4GB
简介: 小胖问我:group by 怎么优化?

04 group by 中使用 where & having


写到这里,有小伙伴就说了。狗哥你这里描述的只是 group by 的单独执行过程,很简单呀。我也会,如果加上 where 或者 having 或者两者都加上的时候的执行过程是怎样的呢?


4.1 group by + where


现在产品又改需求统计每个城市下的下单人数,且下的订单量要大于 2。OS:mmp,又改


按照惯例,看到 where 我们一般想到怎么优化?没错,加索引嘛。


加索引:


alter table sale_order add index idx_order_num (order_num);


最终语句:


select city, count(*) as num from sale_order where order_num > 2 group by city;


结果:


640.png


explain 分析:


640.png


从上图得知,加上索引之后。这条语句命中了索引 idx_order_number,并且此时的 Extra 多了 Using index Condition 的执行计划。type 变成了 range 说明不用全表扫描


解释下 Using index Condition:会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,常见于 where 中有 between > < 等条件的 sql 语句


它的出现说明这个语句先走索引过滤掉不符合 where 条件的数据,再去统计,然后排序,最后返回客户端。流程如下:


  1. 创建内存临时表,表里面有两个字段:city 和 num;


  1. 根据索引 idx_order_num 找到大于 2 的数据的主键 ID;


  1. 通过主键 ID 取出 city = 某城市(比如广州、深圳、上海,囊括你表里涉及到的城市)的记录;


  • 临时表没有 city = 某城市的记录,直接插入,并记为 (某城市,1);
  • 临时表里有 city = 某城市的记录,直接更新,把 num 值 +1。


  1. 重复 2、3 步骤,直至找到所有吗,满足 order_num > 2 的记录。根据 city 字段做排序,然后把结果集返回客户端。


PS:回表的概念我就不说了哈,有兴趣的可以看我之前的《MySQL 索引详解》文章,强烈建议你去看,非常重要的是概念。


4.2 group by + having


现在产品又改需求统计每个城市的下单的人数,且总的下单人数需要在 100 以上。OS:mmp,又改


根据需求很快写出 sql 语句:


select city, count(*) as num from sale_order group by city having num > 100;


640.png


再用 explain 分析一下,得出如下结果:


640.png


哇草,咋回事?跟没加 having 的执行流程一样的?你没看错,其实 having 不直接参与到执行计划中去,它是对结果集操作的,所以这里的加的 having 跟没加是一样的执行计划。画个图,大概就是这样的:


640.png


4.3 group by + where + having


现在产品又改需求统计每个城市的下单超过两单的人数,且总的人数需要在 100 以上。OS:mmmp,又改


按照惯例,我们给 where 条件加上索引:


alter table sale_order add index idx_order_num (order_num);


根据需求很快写出 sql 语句:


select city, count(*) as num from sale_order where order_nunm > 2 group by city having num > 100;


640.png


explain 结果:


640.png


执行流程:


  1. 创建内存临时表,表里面有两个字段:city 和 num;


  1. 根据索引 idx_order_num 找到大于 2 的数据的主键 ID;


  1. 通过主键 ID 取出 city = 某城市(比如广州、深圳、上海,囊括你表里涉及到的城市)的记录;


  • 临时表没有 city = 某城市的记录,直接插入,并记为 (某城市,1);
  • 临时表里有 city = 某城市的记录,直接更新,把 num 值 +1。


  1. 重复 2、3 步骤,直至找到所有吗,满足 order_num > 2 的记录。根据 city 字段做排序。


  1. having 对结果集进行过滤,并返回客户端


不难看出这里的执行流程跟 4.1 一样就多了个 having 过滤


05 group by 优化


根据上面的分析,我们知道 group by 是需要创建临时表并且排序的。耗时也应该在这两个步骤,那我们应该从这两个步骤入手优化。


如果分组字段本身就是有序的,我们是不是就不用排序了?或者我们的需求并没有要求排序是不是就可以优化了?如果必须使用临时表,我们是不是可以只用内存临时表呢?如果数据量实在是太大,是不是可以直接用磁盘临时表,而不是发现内存临时表不够大才用它呢?


以上可以总结出四个优化方案:


  • 分组字段加索引
  • order by null 不排序
  • 尽量使用内存临时表
  • SQL_BIG_RESULT


5.1 分组字段加索引


select city, count(*) as num from sale_order group by city;


上面的 sql 中,city 没加索引,所以这时的 group by 还是要使用临时表的。那我们可不可以个组合索引 idx_city,结果如下所示:


640.png

加索引:


alter table sale_order add index idx_city (city);


结果:


640.png


Extra 是不是 Using temporary 和 Using filesort 都没了?所以不用排序也不用临时表啦。那有小伙伴又问了,那我有 where 条件怎么办?那就加组合索引呗:


alter table sale_order add index idx_order_num_city(order_num,city);


但是这种情况只适用于 where 条件是等值的,如果有大于、小于的情况还是避免不了排序和使用临时表。适用情况:


select city, count(*) as num from sale_order where order_num = 2 group by city;


不适用情况:


select city, count(*) as num from sale_order where order_num > 2 group by city;


5.2 order by null 避免排序


如果需求是不用排序,我们就可以这样做。在 sql 末尾加上 order by null


select city, count(*) as num from sale_order where order_num > 2 group by city order by null;


640.png


从分析结果看,还是需要使用临时表的。


5.3 尽量使用内存临时表


有些小伙伴可能很懵哈,内存临时表是啥?其实 mysql 临时表分内存临时表和磁盘临时表。但是这里就不展开了,有时间专门写一篇文章介绍。


group by 在执行过程中使用内存临时表还是不够用,那就会使用磁盘临时表。内存临时表的大小是有限制的,mysql 中 tmp_table_size 代表的就是内存临时表的大小,默认是 16M。当然你可以自定义社会中适当大一点,这就要根据实际情况来定了。


比如:可以设置成 32M,也就是 33554432 字节。


set tmp_table_size=33554432;


5.4 SQL_BIG_RESULT


如果数据量实在过大,大到内存临时表都不够用了,这时就转向使用磁盘临时表。而发现不够用再转向这个过程也是很耗时的,那我们有没有一种方法,可以告诉 mysql 从一开始就使用 磁盘临时表呢?


有的,在 group by 语句中加入 SQL_BIG_RESULT 提示 MySQL 优化器直接用磁盘临时表。优化器分析,磁盘临时表是 B+ 树存储,存储效率不如数组来得高。所以直接用数组存储。用法如下:


select SQL_BIG_RESULT city, count(*) as num from sale_order where group by city;


640.png


此时的执行过程就不需要创建临时表啦:


  1. 初始化 sort_buffer(排序缓冲区),放入 city 字段;
  2. 扫描 sale_order 表,取出 city 的值存入 sort_buffer 中;
  3. 扫描完成后,对 sort_buffer 的字段 city 做排序(如果 sort_buffer 内存不够用,就会利用磁盘临时文件辅助排序);
  4. 排序完成后,就得到了一个有序数组。
  5. 根据有序数组,得到数组里面的不同值,以及每个值的出现次数


06 group by 面试题


6.1 group by 一定要配合聚合函数使用吗?


不一定,以下 sql 语句,我用的 MySQL 5.7.13 运行是报错的;但是我司的 MySQL 8.0 版本是没有问题的。


select goods_name, city from sale_order group by city;


640.png


出现这个错误的原因是 mysql 的 sql_mode 开启了 ONLY_FULL_GROUP_BY 模式。查看 sql_mode:


select @@GLOBAL.sql_mode;


640.png


如果想要不做限制的话,直接重新设置 sql_mode 的值,把 ONLY_FULL_GROUP_BY 去掉即可。当然,开启这个要慎重,有可能会造成一些意想不到的错误,一般情况下还是加上这个设置比较稳妥。


6.2 group by 后面的一定要出现在 select 中吗?


不一定,我的就没报错。当然,这个还跟版本有关系。大家可以回去自己实践下。


select max(order_num) from sale_order group by city;


640.png


6.1 where & having 的区别?


  • where 用于条件筛选,having 用于分组后筛选


  • where 条件后面不能跟聚合函数,having 一般配合 group by 或者聚合函数(min、max、avg、count、sum)使用


  • where 用在 group by 之前,having 用在 group by 之后
相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
消息中间件 缓存 NoSQL
热点账户高并发记账方案
热点账户高并发记账方案
1804 0
热点账户高并发记账方案
|
10月前
|
存储 SQL 数据库
性能调优:优化 GROUP BY——使用索引字段分组减少临时文件生成
性能调优:优化 GROUP BY——使用索引字段分组减少临时文件生成
647 1
|
12月前
|
机器学习/深度学习 算法 数据挖掘
线性回归模型的原理、实现及应用,特别是在 Python 中的实践
本文深入探讨了线性回归模型的原理、实现及应用,特别是在 Python 中的实践。线性回归假设因变量与自变量间存在线性关系,通过建立线性方程预测未知数据。文章介绍了模型的基本原理、实现步骤、Python 常用库(如 Scikit-learn 和 Statsmodels)、参数解释、优缺点及扩展应用,强调了其在数据分析中的重要性和局限性。
626 3
|
8月前
|
存储 数据采集 数据处理
【数据结构进阶】位图
位图是一种高效的数据结构,通过二进制的0和1表示数据的存在状态,适用于海量数据的压缩存储与快速检索。本文从概念、实现到应用场景全面解析位图。核心思想是将数据映射到位图的比特位,利用位运算实现O(1)时间复杂度的增删查操作。文章通过C++代码示例展示了位图的三大接口(set、unset、test)实现,并对比自定义位图与标准库`bitset`的异同。位图优点在于极高的时间和空间效率,但仅适用于整型数据。它为布隆过滤器等高级结构奠定了基础,在数据处理领域具有重要价值。
537 1
|
SQL Java API
实时计算 Flink版产品使用问题之如何在本地运行和调试包含VVR DataStream连接器的作业
实时计算Flink版作为一种强大的流处理和批处理统一的计算框架,广泛应用于各种需要实时数据处理和分析的场景。实时计算Flink版通常结合SQL接口、DataStream API、以及与上下游数据源和存储系统的丰富连接器,提供了一套全面的解决方案,以应对各种实时计算需求。其低延迟、高吞吐、容错性强的特点,使其成为众多企业和组织实时数据处理首选的技术平台。以下是实时计算Flink版的一些典型使用合集。
|
安全 PHP 数据库
laravel中模型中$fillable的用法
通过正确使用 `$fillable`属性,开发者可以有效地保护应用免受批量赋值漏洞的影响。它使得只有指定的字段可以被外部用户输入影响,为应用数据的安全性提供了一道防线。在开发使用Laravel框架的应用时,恰当地设置 `$fillable`或 `$guarded`属性是一项最佳实践。
346 1
|
机器学习/深度学习 SQL 人工智能
人工智能平台PAI使用问题之如何在阿里云服务器上搭建自己的人工智能
阿里云人工智能平台PAI是一个功能强大、易于使用的AI开发平台,旨在降低AI开发门槛,加速创新,助力企业和开发者高效构建、部署和管理人工智能应用。其中包含了一系列相互协同的产品与服务,共同构成一个完整的人工智能开发与应用生态系统。以下是对PAI产品使用合集的概述,涵盖数据处理、模型开发、训练加速、模型部署及管理等多个环节。
|
移动开发 监控 前端开发
基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化
基于 HTML5 WebGL 和 VR 技术的 3D 机房数据中心可视化
|
存储 Java 网络架构
Spring Boot中如何实现批量处理
Spring Boot中如何实现批量处理
|
域名解析 缓存 安全
cdn服务器连接异常怎么办
当遇到CDN服务器连接异常时,可采取以下步骤排查:检查CDN配置,包括域名解析和防火墙设置;清空CDN缓存;测试网络连接;确认源服务器状态;更换CDN服务器;等待恢复;联系服务商;检查本地电脑安全;检查程序代码;保持更新和维护。具体解决步骤需根据实际情况调整。
2494 0