美文网首页
2018-07-01

2018-07-01

作者: cai182081 | 来源:发表于2018-07-02 08:57 被阅读0次

    SQL优化改写之美--MySQL虚拟列、伪列实现对SQL的优化

    慢SQL文本如下:

    SQL执行时长达38S,获取361条数据结果返回。

    SQL执行计划如下:

    初步索引实现SQL优化:

    由执行计划可知,SQL首先从minute_time(minute_time数据大概估算1440条)表中获取数据,然后嵌套驱动month_show_data(month_show_data数据大概估算333389条),两个表都是全表扫描!可以通过添加索引将SQL优化。

    为两个表添加如下索引:

    ALTER TABLE `minute_time`  ADD INDEX `idx_minute`(`minute`);

    ALTER TABLE `month_show_data` ADD INDEX `idx_sz_type_ct`(`server_zone`, `type`, `create_time`);

    添加索引后,SQL执行情况如下:

    SQL 执行10s左右返回361条结果集。

    添加索引后的执行计划如下:

    由执行计划可知,SQL通过索引idx_minute先从minute_time中过滤出361条数据结果集,然后与通过索引idx_sz_type_ct从month_show_data中过滤出931条数据结果集相关联!

    虽然SQL已经得到优化,但SQL长达10s的执行时间,对业务来说无法接受,随着数据量的增加,SQL执行时间也会越来越慢。

    虚拟列实现SQL优化:

    分析SQL可知,SQL的性能瓶颈在于a.minute = DATE_FORMAT(b.create_time, '%H:%i') 两表之间的关联关系,SQL无法通过表之间的关联关系直接驱动month_show_data直接返回数据。并且MySQL不支持函数索引。无法通过创建函数索引来优化该SQL。

    这时候SQL如何在不改变业务的需求下继续深入优化呢?

    MySQL 5.7 增加了虚拟列的新功能,可以类似的实现Oracle函数索引。由此思路,month_show_data增加虚拟列vr_time,并添加虚拟列索引idx_vr_time。

    ALTER TABLE `month_show_data` ADD vr_time VARCHAR(10) AS (DATE_FORMAT(create_time, '%H:%i')) STORED, ADD KEY idx_vr_time (vr_time);

    并将SQL改写为:

    再次执行,0.27s左右即可返回所需的数据。

    执行计划如下:

    此SQL的优化是否就这样结束了呢?明显不是。

    伪列实现SQL优化:

    由数据量、表之间的关联关系及返回的结果集推断可知,只有在minute_time和month_show_data分别过滤后,再对符合条件的结果集进行关联才是最优的执行计划,而由前面的分析可知,执行计划并没有按照我们想象中方式的实现关联。伪列的引入,可以强制SQL改变表之间的关联顺序,获得想要的执行计划。将SQL改写成如下方式:

    SQL在0.004s之后即返回查询结果。

    执行计划如下:

    分析执行计划可知,SQL先取得DERIVED3结果集(即month_show_data表符合条件的结果集),然后取得DERIVED2结果集(即minute_time表符合条件的结果集),最后以DERIVED2嵌套关联DERIVED3,执行计划达到最优。

    此次SQL的优化难点在于,MySQL并不支持函数索引,在表设计和业务设计的时候,尽量避免对关联谓词和过滤谓词进行函数运算。MySQL 5.7 虚拟列的引入可以在不改动业务实现的情况下,实现函数索引类似的需求。基于代价的优化器存在缺陷,并不能每次都会选出最优的执行计划。能准确分析表之间的关联关系,才是SQL优化的制胜关键!

    相关文章

      网友评论

          本文标题:2018-07-01

          本文链接:https://www.haomeiwen.com/subject/oaehuftx.html