37 什么时候使用内部临时表
37 什么时候使用内部临时表
前面介绍了sort buffer,内存临时表和join buffer,这3个数据结构都是用来存放语句执行过程中的中间数据,以辅助sql语句的执行的,在排序的时候用sort buffer,在使用join语句的时候使用了join_buffer。在mysql里,什么时候使用内部临时表呢?
Union执行流程
create table t37(id int primary key, a int, b int, index(a)); delimiter ;; create procedure idata_t37() begin declare i int; set i=1; while(i<=1000)do insert into t37 values(i, i, i); set i=i+1; end while; end;; delimiter ; call idata_t37(); 执行语句 (select 1000 as f) union (select id from t37 order by id desc limit 2);
这个语句用到了union,语义是取这2个查询结果的并集,重复的只保留一行
--id=2 的key=primary,说明第二个子句用到了索引id
--第三行的extra,在对结果集进行union的时候,使用了临时表(using temporary)
语句执行流程:
--1 创建一个内存临时表,这个临时表只有一个整形字段f,并且f是主键字段
--2 执行第一个子查询,得到1000这个值,并存入临时表中
--3 执行第二个子查询,
---拿到第一行id=1000,试图插入临时表中,但是临时表中已经存在1000这个值,违法了唯一性,所以插入失败,继续执行
---取到id=999,插入临时表成功
--4从临时表中按行取出数据,返回结果,并删除临时表,结果中包含两行数据分表是1000和999
流程如下
可以看到,这里的内存临时表起到了暂存数据的作用,而且计算过程还用上了临时表主键id唯一性约束,实现了union的语义
如果上面改成union all,没有了去重的定义,就依次执行子查询,得到的结果集作为返回的一部分。就不需要临时表了。
--mysql 5.7.22
--这里mysql 5.6.15 版本,使用union all还是用了临时表
{ "creating_tmp_table": { "tmp_table_info": { "table": "intermediate_tmp_table", "row_length": 9, "key_length": 0, "unique_constraint": false, "location": "memory (heap)", "row_limit_estimate": 1864135 } "reconsidering_access_paths_for_index_ordering": { "clause": "ORDER BY", "index_order_summary": { "table": "`t37`", "index_provides_order": true, "order_direction": "desc", "index": "PRIMARY", "plan_changed": true, "access_type": "index_scan"
Group by执行流程
常见的一个使用临时表的group by
select id%10 as m, count(*) as c from t37 group by m;
--using index,表示这个语句使用了覆盖索引,选择了索引a,不需要回表
--using temporary,表示使用了临时表
--using filesort,表示需要排序
执行流程
--1 创建内存临时表,表里有两个字段m和c,主键是m
--2 扫描表t37的索引a,依次取出叶子节点上的id值,计算id%10的结果,记为x
---如果临时表中没有主键为x的行,就插入一个记录(x,1)
--如果表中有主键为x的行,就将x这一行的c值加1
--3 遍历完成后,在根据字段m做排序,得到结果集返回给客户端
上图中最后一步对内存临时表的排序
再查看一个语句的执行结果
如果不需要对结果进行排序,可以在sql语句末尾增加order by null
内存临时表的大小限制,参数tmp_table_size,默认16M
set tmp_table_size=1024;
select id%100 as m, count(*) as c from t37 group by m order by null limit 10;
如果这个表的数据量很大,很可能这个查询需要的磁盘临时表就会占用大量的磁盘空间
Group by 优化方法--索引
可以看到,不论是使用内存临时表还是磁盘临时表,group by逻辑都需要构造一个带唯一索引的表,执行代价都是比较高的,如果表的数据量比较大,上面这个group by语句执行起来就会很慢
要解决group by语句的优化问题,可以先想一想,group by为什么需要临时表?
group by的语义逻辑,是统计不同的值出现的个数,但是,由于每一行的id%100的结果是无序的,所以我们就需要一个临时表,来记录并统计结果。
那么,如果扫描过程中可以保证出现的数据是有序的呢
假设
可以看到,如果可以确保输入的数据是有序的,那么计算group by的时候,就只需要从左到右,顺序扫描,依次累加。
Innodb的索引,就满足这个顺序扫描的条件
在mysql 5.7版本支持了generated column机制,用来实现列数据的关联更新(创建一个列z,然后在z列上创建一个索引,(如果是5.6及之前的版本,可以创建普通列和索引来解决这个问题))。
alter table t1 add column z int generated always as(id % 100), add index(z);
select z, count(*) as c from t1 group by z;
Group by优化方法--直接排序
所以,如果可以通过加索引来完成group by逻辑就再好不过了,但是,如果遇到不适合创建索引的场景,还是要排序,那么这时候的group by要怎么优化呢?
如果我们明明知道,一个group by语句中需要放到临时表上的数据量特别大,却还要按照”先放到内存临时表,插入一部分数据后,发现内存临时表不够了再转成磁盘临时表”
在group by语句中加入SQL_BIG_RESULT这个hint提示,就可以告诉优化器,这个语句设计的数据量很大,请直接用磁盘临时表。
Mysql的优化器一看,磁盘临时表是B+树存储,存储效率不如数组来的高,既然你告诉我数据量很大,那从磁盘空间考虑,还是直接用数组来存吧
select SQL_BIG_RESULT id%100 as m, count(*) as c from t37 group by m;
执行流程
--1 初始化sort_buffer,确定放入一个整型字段,记为m
--2 扫描表t37的索引,依次取出里面的id值,将id%100的值存入sort_buffer
--3 扫描完成后,对sort_buffer的字段m做排序(如果sort_buffer不够用,就会利用磁盘临时文件进行辅助排序)
--4 排序完成后,就得到了一个有序数组
执行流程
从执行计划的extra来看,这个语句的执行没有使用临时表,而是直接使用了排序算法。
Mysql什么时候会使用内部临时表?
--1 如果语句执行过程可以一边读数据,一边直接得到结果,是不需要额外内存的,否则就需要额外的内存,来保存中间结果
--2 join_buffer是无序数组,sort_buffer是有序数组,临时表是二维表结构
--3 如果执行逻辑需要用到二维表特性,就会优先考虑使用临时表,比如我们的例子中,union需要用到唯一索引约束,group by还需要用到另外一个字段来存累计计数。
小结
这篇文章,讲了group by的几种实现算法,
--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的结果。
--explain中id列,如果id不相同,则由大到小,如果id相同,则由上而下。
--在distinct和group by去重中,如果有limit,则distinct要快一些
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构