MySQL45讲 第三十七讲 什么时候会使用内部临时表?——阅读总结

文章目录

MySQL45讲 第三十七讲 什么时候会使用内部临时表?------阅读总结

一、引言

在 MySQL 数据库的执行过程中,内部临时表扮演着重要的角色。已经了解了 sort buffer、内存临时表和 join buffer 等用于辅助 SQL 语句执行的数据结构,那么 MySQL 究竟在何时会使用内部临时表呢?通过 union 和 group by 这两个典型例子深入剖析内部临时表的工作原理,并探讨其使用场景及优化方法。


二、union 执行流程中的内部临时表

(一)示例语句与表结构

sql 复制代码
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();
(select 1000 as f) union (select id from t1 order by id desc limit 2);

我们创建一个包含主键和索引的表 t1,并向其中插入 1000 行数据,用于后续分析。然后执行 (select 1000 as f) union (select id from t1 order by id desc limit 2); 语句。

(二)explain 结果分析

从 explain 结果中可以看到,第二个子句用到了索引 id,而在执行 union 操作时使用了临时表(Extra 字段显示 Using temporary)。

(三)执行流程详解

  1. 首先创建一个只有一个整型字段 f 且 f 为主键字段的内存临时表。
  2. 执行第一个子查询,将 1000 这个值存入临时表。
  3. 执行第二个子查询时,取到 id = 1000 试图插入临时表,因违反唯一性约束失败,继续取到 id = 999 插入成功。
  4. 最后从临时表中按行取出数据返回结果,并删除临时表。

这里的内存临时表利用了主键的唯一性约束实现了 union 的语义,起到了暂存数据的作用。需要注意的是,如果将 union 改为 union all,由于没有 "去重" 语义,执行时直接将子查询结果作为结果集一部分发给客户端,不需要临时表。


三、group by 执行流程中的内部临时表

(一)示例语句与需求

执行 select id%10 as m, count (*) as c from t1 group by m; 语句,其目的是按照 id%10 对表 t1 的数据进行分组统计,并按 m 结果排序输出。

(二)explain 结果分析

Extra 字段显示 Using index(使用覆盖索引)、Using temporary(使用临时表)和 Using filesort(需要排序)。

(三)执行流程详解

  1. 创建内存临时表,包含字段 m 和 c,主键为 m。

  2. 扫描表 t1 的索引 a,计算 id%10 的值记为 x,若临时表中不存在主键为 x 的行则插入 (x, 1),若存在则将 x 行的 c 值加 1。

  3. 遍历完成后,根据字段 m 进行排序,将结果集返回给客户端。

如果不需要对结果排序,可以在 SQL 语句末尾添加 order by null,这样就会跳过排序阶段直接从临时表取数据返回。但当内存临时表大小受 tmp_table_size 参数限制无法容纳数据时,会将其转成磁盘临时表(默认使用 InnoDB 引擎)。


四、group by 的优化方法

不论是使用内存临时表还是磁盘临时表,group by逻辑都需要构造一个带唯一索引的 表,执行代价都是比较高的。如果表的数据量比较大,上面这个group by语句执行起来就会很慢,有什么优化的方法呢?

(一)利用索引优化

  1. 优化思路分析

    group by 语句执行慢的原因是需要构造带唯一索引的临时表来记录和统计结果,因为扫描的数据无序。而 InnoDB 索引可以满足输入有序的条件,从而简化计算过程。

  2. 具体操作示例(MySQL 5.7 及以上)

    通过 **generated column 机制创建列 z 并在其上创建索引(MySQL 5.6 及之前版本可创建普通列和索引),如 alter table t1 add column z int generated always as (id % 100), add index (z);**然后将 group by 语句改为 select z, count (*) as c from t1 group by z;。优化后的 explain 结果显示不再需要临时表和排序。

(二)直接排序优化

  1. 适用场景与提示作用

    当不适合创建索引时,可在 group by 语句中加入 SQL_BIG_RESULT 提示,让优化器直接使用磁盘临时表(实际使用数组存储)。

  2. 执行流程示例

    select SQL_BIG_RESULT id%100 as m, count (*) as c from t1 group by m; 为例,执行流程为初始化 sort_buffer,扫描表 t1 索引 a 并将 id%100 的值存入 sort_buffer ,扫描完成后对 sort_buffer 中的字段 m 排序(内存不够时利用磁盘临时文件辅助排序 ),最后根据有序数组计算不同值及其出现次数。explain 结果显示执行未使用临时表,而是直接使用排序算法。


五、MySQL 使用内部临时表的情况总结

  1. 当语句执行无法边读数据边直接得结果时,需要额外内存保存中间结果,可能会使用内部临时表
  2. join_buffer 是无序数组,sort_buffer 是有序数组,而临时表是二维表结构,若执行逻辑需二维表特性(如 union 需唯一索引约束,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 结果。
相关推荐
凡人的AI工具箱16 分钟前
每天40分玩转Django:Django缓存
数据库·人工智能·后端·python·缓存·django
软茸兔20 分钟前
笔记:一次oracle 集群日志维护
数据库·笔记·oracle
蓝眸少年CY38 分钟前
Win安装PostgreSQL和PostGIS
数据库·postgresql
Tester_孙大壮39 分钟前
运维相关知识科普
大数据·运维·数据库
zhenryx1 小时前
微涉全栈(react,axios,node,mysql)
前端·mysql·react.js
大G哥1 小时前
k8s创建单例redis设置密码
数据库·redis·云原生·容器·kubernetes
ROCKY_8177 小时前
Mysql复习(二)
数据库·mysql·oracle
问道飞鱼9 小时前
【知识科普】认识正则表达式
数据库·mysql·正则表达式
HaiFan.9 小时前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
水根LP499 小时前
linux系统上SQLPLUS的重“大”发现
数据库·oracle