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从临时表中按行取出数据,返回结果,并删除临时表,结果中包含两行数据分表是1000999

流程如下

可以看到,这里的内存临时表起到了暂存数据的作用,而且计算过程还用上了临时表主键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 创建内存临时表,表里有两个字段mc,主键是m

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

---如果临时表中没有主键为x的行,就插入一个记录(x1

--如果表中有主键为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 temporaryusing filesort

--3 如果group by需要统计的数据量不大,尽量只使用内存临时表,也可以通过适当调大tmp_table_size参数,来避免用到磁盘临时表

--4 如果数据量实在太大,使用SQL_BIG_RESULT这个提示,来告诉优化器直接使用排序算法得到group by的结果

--explainid列,如果id不相同,则由大到小,如果id相同,则由上而下

--distinctgroup by去重中,如果有limit,则distinct要快一些

猜你喜欢

转载自www.cnblogs.com/yhq1314/p/10862723.html
37