你知道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:…

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;还没有写入…

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

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

力扣刷题总结 字符串(2)【KMP】

&#x1f525;博客主页&#xff1a; A_SHOWY&#x1f3a5;系列专栏&#xff1a;力扣刷题总结录 数据结构 云计算 数字图像处理 28.找出字符串中第一个匹配项的下标mid经典KMP4593重复的子字符串mid可以使用滑动窗口或者KMP KMP章节难度较大&#xff0c;需要深入理解其中…

Flink 本地单机/Standalone集群/YARN模式集群搭建

准备工作 本文简述Flink在Linux中安装步骤&#xff0c;和示例程序的运行。需要安装JDK1.8及以上版本。 下载地址&#xff1a;下载Flink的二进制包 点进去后&#xff0c;选择如下链接&#xff1a; 解压flink-1.10.1-bin-scala_2.12.tgz&#xff0c;我这里解压到soft目录 [ro…

OrangePi ZERO2 刷机与启动

镜像准备 用读卡器和Win32Diskimager刷写镜像到内存卡&#xff0c;镜像文件见下面百度云链接&#xff1a;https://pan.baidu.com/s/14aKTznc4Jvw4SoFF54JUTg 提取码&#xff1a;1815 刷写完毕后插回香橙派 串口登录 用MobaXterm和USB-TTL进行串口登录&#xff0c;MobaXterm软…

谈一谈网络协议中的应用层

文章目录 一&#xff0c;什么是HTTPHTTP的优缺点HTTPS 一&#xff0c;什么是HTTP 我们在通过网络进行传输数据时&#xff0c;我们要保证&#xff0c;我们在发送时构造的数据&#xff0c;在接收时也能够解析出来&#xff0c;这本质上就是一种协议&#xff0c;是一种应用层协议&…

Spring Cloud + Vue前后端分离-第3章 SpringBoot项目技术整合

Spring Cloud Vue前后端分离-第3章 SpringBoot项目技术整合 3-1 集成持久层框架Mybatis ORM:对象关系映射&#xff0c;Hibernate是全自动ORM&#xff0c;Mybatis是半自动ORM&#xff0c;Mybatis可以操作的花样更多&#xff0c;是首选的持久层框架 System模块集成Mybatis框架…

整数分析 C语言xdoj43

问题描述 给出一个整数n&#xff08;0<n<100000000&#xff09;。求出该整数的位数&#xff0c;以及组成该整数的所有数字中的最大数字和最小数字。 输入说明 输入一个整数n&#xff08;0<n<100000000&#xff09; 输出说明 在一行上依次输出整数n的位…