登录 |  注册
首页 >  数据库 · 存储 >  MySql实战精选笔记 >  什么时候会使用内部临时表

什么时候会使用内部临时表

前面介绍了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);

mysql1.jpg

第二行 key=PRIMARY,用了索引 id

第三行Extra :结果集union用临时表 (Using temporary)。

  1. 创建一个内存临时表,只有整型字段 f,主键。

  2. 第一个子查询,得到 1000 这个值,存入临时表中。

  3. 执行第二个子查询:拿到 id=1000,违反了唯一性约束,插入失败;取第二行 id=999,插入成功。

包含 1000 和 999。

mysql1.jpg

图 2 union 执行流程

union 改成 union all 的话,不去重”的语义。依次子查询。不需临时表。

mysql1.jpg

图 3 union all 的 explain 结果

Using index,表示只使用了覆盖索引,没用临时表。

二、group by 执行流程

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

t1 数据,按id%10 分组统计,按m 结果排序后输出

mysql1.jpg

[endif]不支持该协议取消重新上传图 4 group by 的 explain 结果  

Using index,覆盖索引 a,不需回表;Using temporary临时表;Using filesort,需排序

  1. 创建内存临时表,字段 m (主键)和 c

  2. [扫描 t1 的索引 a,依次取出叶子节点上id 值,计算 id%10 的结果,记为 x;

    临时表中没有主键 x 行,插入(x,1);有,这一行的 c 值加 1;

  3. 遍历完,根据m排序返回

mysql1.jpg

图 5 group by 执行流程

mysql1.jpg

图 6 内存临时表排序流程  (虚线框内)

mysql1.jpg

图 7 group by 执行结果

不需排序,末尾增加 order by null:select id%10 as  m, count(*) as c from t1 group by m order by null;

mysql1.jpg

图 8 group + order by null 的结果(内存临时表)

 t1 中的 id 从 1 开始,第一行是 id=1,临时表只有 10 行,内存放得下,tmp_table_size 控内存大小,默认16M。

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

id % 100,返回结果 100 行不够存,转成磁盘临时表:数据量大,查询磁盘临时表会占用大量磁盘空间。

mysql1.jpg

图 9 group + order by null 的结果(磁盘临时表)  

三、group by 优化方法-- 索引

group by 需要唯一索引,执行代价高。数据量大,group by 慢:统计不同值出现个数。 id%100 结果是无序,需个临时表,记录并统计结果。

mysql1.jpg

图 10 group by 算法优化 - 有序输入

数据有序,group by 的时候,只需左到右,顺序扫描,不需临时表和排序:

碰到第一个1 时,已知累积 X 个 0,结果集第一行(0,X);

碰到第一个 2 时,已知累积Y 个 1,结果集第二行(1,Y);

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);

索引 z 上有序, group by 改成:select z,  count(*) as c from t1 group by z;

Extra 看到,不再需要临时表和排序

mysql1.jpg

图 11 group by 优化的 explain 结果  

四、group by 优化方法-- 直接排序

不适合建索引场景,怎么优化呢?放到临时表数据量大,直接走磁盘临时表

group by 加SQL_BIG_RESULT 提示(hint)优化器:量大用磁盘临时表。

磁盘临时表B+ 树存储,存储效率不如数组高。直接用数组存。

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 排序(内存不够,用磁盘临时文件辅助排序);

  4.  排序完成后,得有序数组(不同值,每个值出现次数)。

mysql1.jpg

图 12 使用 SQL_BIG_RESULT 的执行流程图

mysql1.jpg

图 13 使用 SQL_BIG_RESULT 的 explain 结果

Extra :没用临时表,直接用排序算法。

什么时候用内部临时表

1. 可边读数据,边得结果,不额外内存,否则就需额外内存,保存中间结果;

2.  用到二维表特性,用临时表。union用到唯一索引约束, group by 用另外一个字段存累积计数。

join_buffer 是无序数组,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 结果

上一篇: MySQL中自增ID用完了怎么办
下一篇: 为什么MySQL添加索引后就可以提高查询速度
推荐文章
  • mysql只支持一种join算法:Nested-LoopJoin(嵌套循环连接),但Nested-LoopJoin有三种变种:SimpleNested-LoopJoin,IndexNested-LoopJoin,BlockNested-LoopJoin(简单-索引-缓冲区)原理:1.SimpleNe
  • redis是一个内存数据库,一旦断电或服务器进程退出,内存数据库中的数据将全部丢失,所以需要redis持久化 redis持久化就是把数据保存在磁盘上,利用永久性存储介质将数据保存,在特定的时间将保存的数据进行恢复的工作机制redis提供两种持久化机制RDB:存储数据结果,关注点在数据AOF:存储操作
  • 通过SQL的执行过程来介绍MySQL的基础结构.     首先有一个user_info表,表里有一个id字段,执行下面这条查询语句:Select * form user_info where i
  • 索引(Index)是帮助MySQL高效获取数据的数据结构,索引的目的在于提高查询效率,就像字典和书籍的目录一样,有了目录,可以帮助你快速查找你需要的内容。可以理解为一个排好序的快速查找数据结构。也就是
  • 说到数据库事务,大家脑子里一定很容易蹦出一堆事务的相关知识,如事务的ACID特性,隔离级别,解决的问题(脏读,不可重复读,幻读)等等,但是可能很少有人真正的清楚事务的这些特性又是怎么实现的,为什么要有四个隔离级别。今天我们就先来聊聊MySQL中事务的隔离性的实现原理,后续还会继续出文章分析其他特性的
  • 前面我们系统了解了一个查询语句的执行流程,并介绍了执行过程中涉及的处理模块。相信你还记得,一条查询语句的执行过程一般是经过连接器、分析器、优化器、执行器等功能模块,最后到达存储引擎。那么,一条更新语句
学习大纲