order by如何工作join如何使用中分别了解了sort_buffer、内存临时表和join_buffer。这三个数据结构都是用来存放语句执行过程中的中间数据,以辅助SQL语句的执行。

排序的时候使用sort_buffer,join的时候使用join_buffer,那么内部临时表合适使用?

union执行流程

union的语义是,取两个子查询结果的并集。并集的意思就是这两个集合加起来,重复的行只保留一行。

(select 1000 as f) union (select id from t1 order by id desc limit 2);

这个语句的执行流程是这样的:

  1. 创建一个临时表,这个临时表只有一个整形字段f,并且f是主键字段
  2. 执行第一个子查询,得到1000这个值,存入临时表中
  3. 执行第二个子查询,不重复的数据将成功插入。
  4. 从临时表中按行取出数据。

这个过程中,内存临时表起到了暂存数据的作用。但是如果union换成union all的话,不需要去重,那么自然也就不需要内存临时表了。

group by执行流程

在group by中使用临时表也很常见。

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

执行流程如下:

  1. 创建内存临时表,表里有两个字段m和c,主键是m。
  2. 扫描t1的索引a,依次取出叶子节点上的id的值,计算id%10的结果,记为x
    1. 如果临时表中没有主键为x的行,就插入一个记录(x,1)
    2. 如果表中有主键为x的行,就将x这一行的c值加1。
  3. 遍历完成后,再根据字段m做排序,得到的结果集返回给客户端。

如果需求不需要排序,可以在sql末尾增加order by null,这样就跳过了排序阶段,直接从临时表返回数据。

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

内存临时表的大小是有限制的,参数 tmp_table_size 就是控制这个内存大小的,默认是 16M。内存临时表不够用就会转为磁盘临时表,默认使用InnoDB引擎。

group by优化——索引

group by语义的逻辑,是统计不同的值出现的个数,因为前面的id%100的结果是无序的,所以就需要有一个临时表,来记录并统计结果。

如果扫描过程中可以保证出现的数据是有序的,那么就简单了。InnoDB的索引就能够满足这个条件。

5.7可以创建关联列,我们就利用关联列和索引来更改前面的group by语句:

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语句需要放到临时表的数据量很大,在内存临时表不够的时候会转为磁盘临时表,可以考虑直接走磁盘临时表。使用SQL_BIG_RESULT这个提示,就可以告诉优化器,这个语句设计的数据量很大,请直接使用磁盘临时表。

小结

mysql使用内部临时表的场景:

  1. 如果语句执行过程中可以一边读数据,一边得到结果,是不需要额外内存的,否则就需要额外的内存,来保存中间结果。
  2. join_buffer是无序数组,sort_buffer是有序数组,临时表是二维表结构。
  3. 如果执行逻辑需要用到二维表特性,就会优先考虑使用临时表。比如我们的例子中,union需要使用到唯一索引约束,group by还需要用到另外一个字段来存累积计数。