zoukankan      html  css  js  c++  java
  • 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要快一些

  • 相关阅读:
    css 图片的无缝滚动
    有时间研究下这个
    js的类数组对象
    js的this什么时候会出现报错
    js前端分页
    js队列
    js前端处理url中的参数为对象
    随机看的一点代码
    js的callee和caller方法
    js的Object和Function
  • 原文地址:https://www.cnblogs.com/yhq1314/p/10862723.html
Copyright © 2011-2022 走看看