一个标准的 Group by 语句包含排序、分组、聚合函数,比如 select a,count(*) from t group by a ;  这个语句默认使用 a 进行排序。如果 a 列没有索引,那么就会创建临时表来统计 a和 count(*),然后再通过 sort_buffer 按 a 进行排序。

标准的执行流程

结构:

create table t1(id int primary key, a int, b int, index(a));
delimiter ;;
create procedure idata()
begin
declare i int; set i=1;
while(i<=1000)do
insert into t1 values(i, i, i);
set i=i+1;
end while;
end;;
delimiter ;
call idata();

函数就是向 t1 中插入1000条语句,从(1,1,1) 到(1000,1000,1000)。

执行   select id%10 as m, count(*) as c from t1 group by m;

解析:

Using index,表示这个语句使用了覆盖索引,选择了索引 a,不需要回表;
Using temporary,表示使用了临时表;
Using filesort,表示需要排序。

过程:

1、创建内存临时表,表里有两个字段 m 和 c,主键是 m;
2、扫描表 t1 的索引 a,依次取出叶子节点上的 id 值,计算 id%10 的结果,记为 x;
  1)如果临时表中没有主键为 x 的行,就插入一个记录 (x,1);
  2)如果表中有主键为 x 的行,就将 x 这一行的 c 值加 1;

第2 步如果发现内存临时表存储的总字段长度到达参数 tmp_table_size 设置的大小,那么就会将内存临时表升级为磁盘临时表,然后重新开始遍历计算。
3、遍历完成后,再根据字段 m 做排序,得到结果集返回给客户端。

最后的排序就是下图虚线框中的操作,如果 sort_buffer 设置的大小不够大,那么就会使用临时表来辅助排序。

优化

未优化(也就是分组列没有索引)的 group by 的总过程可以概括为:因为数据是无序的,所以需要创建临时表,然后一个一个判断属于哪个分组,最后再根据分组列进行排序。所以,优化可以有两个思路:

去掉排序

在明确返回的数据不需要排序的情况下,可以禁止排序,也就是将上面的语句改成 select a,count(*) from t group by a order by null。

顺序排列

如果记录都按照排序字段排序,那么数据就变成了下面的结构:

这样在实际获取要返回的字段或计算聚合函数时,只需要按顺序依次访问,等到列值变成下一个就知道当前组访问结束,将之前统计的数据直接返回。这样就避免了创建临时表,同时排序也不需要使用 sort_buffer 进行额外排序。这样就极大地提高了执行的效率。

实现

1、如果分组字段适合创建索引就直接为分组字段创建索引。

MySQL 5.7 版本支持了 generated column 机制,用来实现列数据的关联更新。你可以用下面的方法创建一个列 z,然后在 z 列上创建一个索引(如果是 MySQL 5.6 及之前的版本,你也可以创建普通列和索引,来解决这个问题)

alter table t1 add column z int generated always as(id % 100), add index(z);

然后解析:

这时没有用到临时表和额外排序,所以性能提升。

2、如果分组字段不适合(使用率很低),那么可以使用 SQL_BIG_RESULT 来尝试优化。

在 group by 语句中加入 SQL_BIG_RESULT 这个提示(hint),就可以告诉优化器:这个语句涉及的数据量很大,请直接用磁盘临时表。MySQL 的优化器一看,磁盘临时表是 B+ 树存储,存储效率不如数组来得高。所以,既然使用SQL_BIG_RESULT来说明数据量很大,那从磁盘空间考虑,还是直接用数组来存吧。所以在使用 SQL_BIG_RESULT 后优化器会使用数组结构的磁盘临时表。

但是如果在未达到磁盘临时表的使用条件是不会使用磁盘临时表的,也就是在 sort_buffer 空间能够存储要返回和排序的总字段长度时,就使用数组结构的 sort_buffer ,如果总字段超过 sort_buffer 大小,那么就再加上数组结构的磁盘临时表来帮助排序。

那么在 sort_buffer 空间足够的情况下, sort_buffer 内部就会对数据进行排序,这样也就起到了索引的作用,

还是以上面的例子来看,使用 SQL_BIG_RESULT

alter table t1 add column z int generated always as(id % 100), add index(z);

具体过程如下:

1、初始化 sort_buffer,确定放入一个整型字段,记为 m;
2、扫描表 t1 的索引 a,依次取出里面的 id 值, 将 id%10 的值存入 sort_buffer 中;
3、扫描完成后,对 sort_buffer 的字段 m 做排序(如果 sort_buffer 内存不够用,就会利用磁盘临时文件辅助排序);
4、排序完成后,就得到了一个有序数组。

解析:

可以看到此时就没有使用临时表了,而是直接使用 sort_buffer 进行排序,这样就省去了使用临时表带来的性能消耗。

总结

1、如果对 group by 语句的结果没有排序要求,要在语句后面加 order by null;那么一般情况就不需要使用临时表了(上面两个优化都是在要求排序的前提下提出的优化方式)
2、尽量让 group by 过程用上表的索引,确认方法是 explain 结果里没有 Using temporary 和 Using filesort;
3、如果 group by 需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大 tmp_table_size 参数,来避免用到磁盘临时表;
4、如果数据量实在太大,使用 SQL_BIG_RESULT 这个提示,来告诉优化器直接使用排序算法得到 group by 的结果。

Group by 优化的更多相关文章

  1. Mysql查询优化汇总 order by优化例子,group by优化例子,limit优化例子,优化建议

    Mysql查询优化汇总 order by优化例子,group by优化例子,limit优化例子,优化建议 索引 索引是一种存储引擎快速查询记录的一种数据结构. 注意 MYSQL一次查询只能使用一个索引 ...

  2. Hive:表1inner join表2结果group by优化

    问题背景 最近遇到一个比较棘手的事情:hive sql优化: lib表(id,h,soj,noj,sp,np)         --一个字典表 mitem表(md,mt,soj,noj,sp,np)- ...

  3. 6.4 group by 优化

    1.小总结 group by 实质是先排序后进行分组,遵照索引建的最佳左前缀. 当无法使用索引列,增大max_length_for_sort_data参数的设置 + 增大sort_buffer_siz ...

  4. MySQL高级 之 order by、group by 优化

    参考:  https://blog.csdn.net/wuseyukui/article/details/72627667 order by示例 示例数据: Case 1 Case 2 Case 3 ...

  5. mysql group by优化

    mysql> explain select actor.first_name,actor.last_name,count(*) from sakila.film_actor inner join ...

  6. distinct用group by优化

    当数据量非常大,在同一个query中计算多个不相关列的distinct时,往往很容易出现数据倾斜现象,导致运行半天都不能得到结果. 比如以下的SQL语句(a, b, c没有相关性): select d ...

  7. Mysql group by,order by,dinstict优化

    1.order by优化 2.group by优化 3.Dinstinct 优化 1.order by优化 实现方式: 1. 根据索引字段排序,利用索引取出的数据已经是排好序的,直接返回给客户端: 2 ...

  8. ORDER BY,GROUP BY 和DI STI NCT 优化

    读<MySQL性能调优与架构设计>笔记之ORDER BY,GROUP BY 和DI STI NCT 优化 2015年01月18日 18:51:31 lihuayong 阅读数:2593 标 ...

  9. [MySQL Reference Manual] 8 优化

    8.优化 8.优化 8.1 优化概述 8.2 优化SQL语句 8.2.1 优化SELECT语句 8.2.1.1 SELECT语句的速度 8.2.1.2 WHERE子句优化 8.2.1.3 Range优 ...

随机推荐

  1. 从零开始部署发布Java项目到服务器的Docker中

    本以为很简单,由于没用过docker和java,本文将会阐述一路遇到的拦路虎. 首先,写好Java项目,本地跑通. 可能会遇到的问题: 1.jar包正常运行,war包404: 如何打包很多教程都有介绍 ...

  2. 基于SpringBoot的博客项目

    SpringBoot 博客系统 后端: 框架:SpringBoot 安全框架:shiro 数据库:mysql ORM:mybaits mybaits-plus 插件:lombok 模板引擎:thyme ...

  3. 高性能MySQL学习总结二----常见数据类型选择及优化

    一.数据类型的选择 MySQL的数据类型有很多种,选择正确的数据类型对于获得高性能特别地重要,如何选择合适的数据类型呢?主要遵从以下三个原则: 1.更小的通常情况下性能更好 一般情况下,应该尽量使用可 ...

  4. 分享知乎关于pull request的分享

    作者:知乎用户链接:https://www.zhihu.com/question/21682976/answer/79489643来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  5. @component的作用详细介绍

    最近项目要采用spring boot在学习的spring boot 的过程中第一次见到@component注解,特意在网上搜索下,摘录在此方便日后查阅. 1.@controller 控制器(注入服务) ...

  6. hadoop集群中zkfc的作用和工作过程

    一. 简单了解NameNode的ZKFC机制 NameNode的HA可以个人认为简单分为共享editLog机制和ZKFC对NameNode状态的控制 一般导致NameNode切换的原因 ZKFC的作用 ...

  7. UnRAID_6.8.2_配置_设置

    UnRAID_6.8.2_配置_设置 转载注明来源: 本文链接 来自osnosn的博客,写于 2020-10-05. 参考: UnRAID download Getting_Started Offic ...

  8. 多图详解Go中的Channel源码

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com 本文使用的go的源码时14.4 chan介绍 package main import & ...

  9. 【Java基础】Java8 新特性

    Java8 新特性 Lambda 表达式 Lambda 是一个匿名函数,我们可以把 Lambda 表达式理解为是一段可以传递的代码(将代码像数据一样进行传递).使用它可以写出更简洁.更灵活的代码. L ...

  10. linux常用命令--转载

    转载自: https://www.cnblogs.com/Qsunshine/p/10402179.html 常用指令 ls 显示文件或目录 -l列出文件详细信息l(list) -a列出当前目录下所有 ...