你知道MySQL中 group by 怎么优化吗

更好的阅读体验,请点击 YinKai 's Blog。

​ 在 MySQL 中 group by 用于按照一个或多个列对结果集进行分组。在讨论 group by 怎么优化之前,我们先来看看 group by 的执行流程,这样我们才能对症下药。

group by 执行流程

​ 我们先用下面的 sql 语句创建一个表,并输入一些数据,模拟真实环境。

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

​ 然后我们执行下面的语句:

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

​ 这个语句的逻辑是把表 t1 里的数据,按照 id%10 进行分组统计,并按照 m 的结果排序后输出。它的 explain 结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 在 Extra 字段我们可以看到三个信息:

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

​ 这个语句的执行过程是:

  1. 创建内存临时表,表里有两个字段 m 和 c,主键是 m
  2. 扫描表 t1 的索引 a,依次取出叶子结点上的 id 值,计算 id%10 的结果,记为 x;
    • 如果临时表中没有主键为 x 的行,就插入一个记录 (x, 1);
    • 如果临时表中有主键为 x 的行,就将 x 这一行的 c 值加 1;;
  3. 遍历完成后,再根据字段 m 做排序,得到的结果返回给客户端。

​ 这个流程的执行图如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 图中最后一步,对内存临时表的排序过程如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 上面的例子,由于临时表只有 10 行,内存可以放得下,因此只使用了内存临时表。但内存临时表是有大小限制的,可以通过参数 tmp_table_size 修改,默认是 16M。

​ 如果我执行下面这个语句序列:

set tmp_table_size=1024;
select id%100 as m, count(*) as c from t1 group by m order by null limit 10;

​ 把内存临时表的大小限制为最大 1024 字节,并把语句改成 id % 100,这样返回结果里有 100 行数据。但是,这时的内存临时表大小不够存下这 100 行数据,也就是说,执行过程中会发现内存临时表大小到达了上限(1024 字节)。

​ 那么这时候就会把内存临时表转成磁盘临时表,磁盘临时表默认使用的是 InnoDB,结果如下:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 如果这个表 t1 的数据量很大,很可能这个查询的磁盘临时表需要用到很大的磁盘空间,查询生成大型临时表,占用大量磁盘空间可能导致查询变慢,引起磁盘空间不足,影响系统稳定性。

​ 因此,这就是为什么我们需要去优化 group_by 的原因。

group by 优化方法——索引

​ 要解决 group by 的优化问题,我们需要从根本上去解决问题,即执行 group by 语句创建的临时表。

​ group by 的语义逻辑,是统计不同的值出现的个数。但是由于每一行的 id%100 的结果是无序的,所以我们就需要有一个临时表,来记录并统计结果。

​ 那我们假想出现的数据都是有序的,看看 group by 会怎么做。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 如果我们可以确保输入的数据都是有序的,那么计算 group by 的时候,就只需要从左往右顺序扫描,依次累加,即:

  • 当碰到第一个 1 的时候,已经知道累积了 X 个 0,结果集里的第一行就是 (0,X);
  • 当碰到第一个 2 的时候,已经知道累积了 Y 个 1,结果集里的第二行就是 (1,Y);

​ 按照这个逻辑执行的话,扫描到整个输入的数据结束,就可以拿到 group by 的结果,不需要临时表,也不需要再额外排序。

​ 不难想到,InnoDB 的索引就可以满足这个输入有序的条件。

​ 我们可以 MySQL5.7 版本的 generated column 机制,用来实现列数据的关联更新。你可以用下面的方法创建一个列 z,然后在 z 列上创建一个索引

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

​ 这样,索引 z 上的数据就是类似上图那样有序的了。上面的 group by 语句就可以改成:

select z, count(*) as c from t1 group by z;

​ 优化后的 group by 语句的 explain 结果,如下图所示:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 从 Extra 字段可以看出,这个语句的执行不再需要临时表了,也不需要排序了。

group by 优化方法 – 直接排序

​ 如果能使用创建索引的方式来优化那再好不过,万一要是遇到了不适合创建索引的创建,我们又该怎么办呢?

​ 当我们明确知道一个 GROUP BY 语句中涉及的数据量非常大,而 MySQL 的默认行为是首先尝试在内存中创建临时表,然后在内存不足的情况下将其转为磁盘临时表,我们可能希望直接走磁盘临时表的方式,以避免不必要的内存消耗。MySQL 提供了一个查询提示 SQL_BIG_RESULT 来实现这一点。

具体而言,你可以在 GROUP BY 语句中加入 SQL_BIG_RESULT 提示,告诉优化器:由于数据量较大,请直接使用磁盘临时表。这样,优化器会考虑在磁盘上存储临时表,而不是首先尝试在内存中完成这一操作。

以下是使用 SQL_BIG_RESULT 提示的一个示例:

SELECT SQL_BIG_RESULT id % 100 AS m, COUNT(*) AS c FROM t1 GROUP BY m;

这个查询的执行流程可以描述为:

  1. 初始化 sort_buffer,确定放入一个整型字段 m
  2. 扫描表 t1 的索引 a,依次取出其中的 id 值,将 id % 100 的值存入 sort_buffer 中。
  3. 扫描完成后,对 sort_buffer 的字段 m 进行排序。如果 sort_buffer 内存不足,将会利用磁盘临时文件辅助排序。
  4. 排序完成后,得到一个有序数组。
  5. 根据有序数组,获取数组中的不同值以及每个值的出现次数。

​ 这样,通过使用 SQL_BIG_RESULT 提示,你可以明确告知 MySQL 优化器,考虑到数据量很大,直接使用磁盘临时表。

​ 执行 explain 的结果如下图:

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

​ 从 Extra 字段可以看到,这个语句的执行没有再使用临时表,而是直接用了排序算法。

小结

​ 现在我们来总结一下使用 group by 需要注意的一些点:

  1. 如果对 group by 语句的结果没有排序要求,要在语句后面加 order by null;
  2. 使用 group by 的时候,尽可能用上表的索引,确认的方法是查看 explain 结果里有没有 Using temporary 和 Using filesort;
  3. 如果 gruop by 需要统计的数据量不大,尽量只使用内存临时表;也可以通过适当调大 tmp_table_size 参数避免使用磁盘临时表;
  4. 如果数据量实在太大,使用 SQL_BIG_RESULT 这个提示,来告诉优化器直接使用排序算法得到 group by 的结果。

​ 最后,我们来看一看文章开头的问题:

​ MySQL中 group by 怎么优化?

  1. 尽可能保证 group by 语句上存在索引,这样有助于数据引擎更有效地执行分组操作,我们可以通过查看执行计划 explain 的输出,来确认是否使用了索引。
  2. 如果内存临时表足够容纳 group by 的结果集的话,可以适当增加内存临时表的参数大小,使 MySQL 更倾向于使用内存临时表,因为内存的读写速度远高于磁盘,这样可以显著提高查询性能。
  3. 如果 GROUP BY 的字段是通过某个表达式计算而来,考虑使用生成列,并在生成列上创建索引。
  4. 在 GROUP BY 的数据量非常大且无法通过其他手段优化时,可以使用 SQL_BIG_RESULT 提示,**让优化器直接使用排序算法而不是创建临时表,**这样 MySQL 就可以直接通过遍历数组获取我们想要的结果。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/211567.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Ubuntu 18.04使用Qemu和GDB搭建运行内核的环境

安装busybox 参考博客&#xff1a; 使用GDBQEMU调试Linux内核环境搭建 一文教你如何使用GDBQemu调试Linux内核 ubuntu22.04搭建qemu环境测试内核 交叉编译busybox 编译busybox出现Library m is needed, can’t exclude it (yet)的解释 S3C2440 制作最新busybox文件系统 https:…

block-recurrent-transformer-pytorch 学习笔记

目录 有依赖项1&#xff1a; 没有依赖项&#xff0c;没有使用例子 没有依赖项2&#xff1a; 有依赖项1&#xff1a; GitHub - dashstander/block-recurrent-transformer: Pytorch implementation of "Block Recurrent Transformers" (Hutchins & Schlag et a…

gd32和stm32的区别

gd32和stm32的区别 现在的市场上有很多种不同类型的微控制器&#xff0c;其中比较常见的有两种&#xff0c;即gd32和stm32。两种微控制器都是中国和欧洲的两个公司分别推出的&#xff0c;但是它们之间有很多区别&#xff0c;本文将会深入探讨这些区别。 1.起源和历史 gd32是…

2024年网络安全竞赛-Web安全应用

Web安全应用 (一)拓扑图 任务环境说明: 1.获取PHP的版本号作为Flag值提交;(例如:5.2.14) 2.获取MySQL数据库的版本号作为Flag值提交;(例如:5.0.22) 3.获取系统的内核版本号作为Flag值提交;(例如:2.6.18) 4.获取网站后台管理员admin用户的密码作为Flag值提交…

udp多播组播

import socket ,struct,time# 组播地址和端口号 MCAST_GRP 239.0.0.1 MCAST_PORT 8888 # 创建UDP socket对象 sock socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) # 绑定socket对象到本地端口号 # sock.bind((MCAST_GRP, MCAST_PORT)) …

【4】PyQt输入框

1. 单行文本输入框 QLineEdit控件可以输入单行文本 from PyQt5.QtWidgets import QApplication, QWidget, QLineEdit, QVBoxLayout from PyQt5.QtCore import * from PyQt5.QtGui import QIcon import sysdef init_widget(w: QWidget):# 修改窗口标题w.setWindowTitle(单行输…

前端面试——CSS面经(持续更新)

1. CSS选择器及其优先级 !important > 行内样式 > id选择器 > 类/伪类/属性选择器 > 标签/伪元素选择器 > 子/后台选择器 > *通配符 2. 重排和重绘是什么&#xff1f;浏览器的渲染机制是什么&#xff1f; 重排(回流)&#xff1a;当增加或删除dom节点&…

【面试经典150 | 二叉树】从中序与后序遍历序列构造二叉树

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a;递归 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的数据结构等内容…

Android : Room 数据库的基本用法 —简单应用

1.Room介绍&#xff1a; Android Room 是 Android 官方提供的一个持久性库&#xff0c;用于在 Android 应用程序中管理数据库。它提供了一个简单的 API 层&#xff0c;使得使用 SQLite 数据库变得更加容易和方便。 以下是 Android Room 的主要特点&#xff1a; 对象关系映射…

9.MySQL 索引

目录 ​​​​​​​概述 概念&#xff1a; 单列索引 普通索引 创建索引 查看索引 删除索引 唯一索引 创建唯一索引 删除唯一索引 主键索引 组合索引 创建索引 全文索引 概述 使用全文索引 空间索引 内部原理 相关算法&#xff1a; hash算法 二叉树算法 …

Spring基于XML文件配置AOP

AOP AOP&#xff0c;面向切面编程&#xff0c;是对面向对象编程OOP的升华。OOP是纵向对一个事物的抽象&#xff0c;一个对象包括静态的属性信息&#xff0c;包括动态的方法信息等。而AOP是横向的对不同事物的抽象&#xff0c;属性与属性、方法与方法、对象与对象都可以组成一个…

12.10多种编码方式,编码方案选择策略(递归级联),PDE,RLE代码

作者如何选择和设计编码方案&#xff0c;以实现高效的解压缩和高压缩比&#xff1f;BtrBlocks是否适用于所有类型的数据&#xff1f; 选择和设计编码方案&#xff1a; 结合多种高效编码方案&#xff1a;BtrBlocks 通过选择一组针对不同数据分布的高效编码方案&#xff0c;实现…

js判断是否对象自身为空

文章目录 一、前言二、JSON.stringify三、for in 配合 hasOwnProperty四、Object.keys五、Object.getOwnPropertyNames六、Object.getOwnPropertyNames 结合 Object.getOwnPropertySymbols七、Reflect.ownKeys八、最后 一、前言 如何判断一个对象为空&#xff1f; 先上结论&a…

MySql复习笔记03(小滴课堂) 事务,视图,触发器,存储过程

mysql 必备核心知识之事务的详细解析&#xff1a; 创建一个数据库表&#xff1a; 添加数据并开启事务。 添加数据并查询。 登录另一台服务器发现查不到这个表中的数据。 这是因为事务开启了&#xff0c;但是没有提交&#xff0c;只是把数据存到了内存中&#xff0c;还没有写入…

以为回调函数是同步的(js的问题)

回调函数可以用来处理 JavaScript 的异步操作&#xff0c;但是选用 Promise、async/await 更好&#xff0c;因为多重回调函数会导致回调地狱。 回调函数不是**同步的**&#xff0c;它是延时操作执行完毕后会被调用的一个函数。 比如全局方法 "setTimeout" &#xf…

CString 的 Replace 函数

Replace 使用测试 CString mSectNameNew L"槽a*b*c*d";CString mSectNameNew2 L"Ca*b*c*d";CString mSectNameNew3 L"[a*b*c*d";mSectNameNew.Replace(_T("M"), _T("C")); // 不会替换mSectNameNew.Re…

JOSEF 冲击继电器 ZC-23A DC48V 柜内安装,板前带座

系列型号 ZC-23冲击继电器&#xff1b;ZC-23A冲击继电器&#xff1b; ZC-23B冲击继电器 一、用途 冲击继电器ZC-23A DC48V 柜内安装板前带座 (以下简称继电器)&#xff0c;广泛用于直流操作的继电器保护及自动控制回路中&#xff0c;作为集中控制信号元件。 二、主要技术参…

C#动态调用C++DLL中的函数

DLL中导出的函数 typedef void (*HQ_MSG_CALLBACK)(void *h, int nMsg, int nMsgType, int nReqNo, const char *szData, int nSize); void SetMsgFunc(void *h, HQ_MSG_CALLBACK pmsgCallBack);C#动态调用上述函数 public delegate void CALLBACK(IntPtr h, int nMsg, int n…

信息处理技术员

目录 信息处理技术员工作内容 信息处理技术员岗位面试试题举例 信息处理技术员考试 信息处理技术员工作内容 信息处理技术员是负责处理和管理信息系统的专业人员。他们的主要工作内容包括以下几个方面&#xff1a; 1.系统维护和管理&#xff1a;信息处理技术员负责维护和管…