MySQL · 性能优化 · 条件下推到物化表

http://mysql.taobao.org/monthly/2016/07/08/

背景

MySQL引入了Materialization(物化)这一关键特性用于子查询(比如在IN/NOT IN子查询以及 FROM 子查询)优化。
具体实现方式是:在SQL执行过程中,第一次需要子查询结果时执行子查询并将子查询的结果保存为临时表 ,后续对子查询结果集的访问将直接通过临时表获得。
与此同时,优化器还具有延迟物化子查询的能力,先通过其它条件判断子查询是否真的需要执行。物化子查询优化SQL执行的关键点在于对子查询只需要执行一次。 与之相对的执行方式是对外表的每一行都对子查询进行调用,其执行计划中的查询类型为“DEPENDENT SUBQUERY”。

在使用Materialization(物化)能提高SQL性能的同时,也有必要留意相关SQL是否存在进一步优化空间的可能性。比如下面描述的场景:

  1. mysql>explain extended Select * from (select * from score where score >= 60) derived1 where class_id = 10;
  2. +----+-------------+------------+-------+---------------+-------------+---------+-------+------+----------+--------------------------+
  3. | id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
  4. +----+-------------+------------+-------+---------------+-------------+---------+-------+------+----------+--------------------------+
  5. | 1 | PRIMARY | <derived2> | ref | <auto_key0> | <auto_key0> | 4 | const | 0 | 100 | |
  6. | 2 | DERIVED | score | index | idx_score | idx_score | 4 | | 1 | 100 | Using where; Using index |
  7. +----+-------------+------------+-------+---------------+-------------+---------+-------+------+----------+--------------------------+

从执行计划可看出,MySQL首先物化了子查询(select_type=DERIVED,或者以format=json格式查看执行计划),然后再通过class_id字段对结果集进行过滤。这个SQL从语义上,也可以写成如下形式,若索引合理执行效率会更高。

  1. select * from score where score >= 60 and class_id=10

从这个例子可以看出子查询物化时的一个潜在问题:当子查询本身比较耗费资源或结果集较大时,往往存在较高的优化空间,特别是在外层条件可作用于子查询的情况下。通过条件下推,在执行过程中尽早减少数据访问量,能显著提高性能。本文重点描述将条件下推到物化子查询的场景。

分析

事实上前面提到的查询在5.7版本可以自动重写。打开优化器选项 derived_merge=on 后,查看重写后的语句如下:

  1. select `remall`.`score`.`class_id` AS `class_id`,`remall`.`score`.`student_id` AS `student_id`,`remall`.`score`.`score` AS `score`
  2. from `remall`.`score`
  3. where ((`remall`.`score`.`class_id` = 10) and (`remall`.`score`.`score` >= 60))

另一方面,并不是所有子查询可以做到自动条件下推。比如下面这个语句:

  1. select * from (select class_id, avg(score) from score group by class_id) derived1 where class_id = 10;

出现这种现象的原因是MySQL优化器目前只能对Mergable的视图或子查询进行重写。理解这一概念可以先从视图的两种算法入手:merge 和 temptable。

一般较为复杂的视图或子查询会使用temptable算法类型,包括:
1. 聚合子查询;
2. 含有LIMIT的子查询;
3. UNION 或UNION ALL子查询;
4. 输出字段中的子查询;

我们也可以显示的通过创建视图来判断子查询是否使用了merge算法。 比如:

  1. mysql>create algorithm=merge view v as select class_id, avg(score) from score group by class_id;
  2. 执行成功,花费 2.46 ms.
  3. mysql>show warnings;
  4. +---------+------+-------------------------------------------------------------------------------+
  5. | Level | Code | Message |
  6. +---------+------+-------------------------------------------------------------------------------+
  7. | Warning | 1354 | View merge algorithm can't be used here for now (assumed undefined algorithm) |
  8. +---------+------+-------------------------------------------------------------------------------+

我们创建视图时指定使用merge,但是数据库判定该算法不适合因此使用默认的undefined(实际执行过程中使用temptable算法)。

  1. /**
  2. Strategy for how to process a view or derived table (merge or materialization)
  3. */
  4. enum enum_view_algorithm {
  5. VIEW_ALGORITHM_UNDEFINED = 0,
  6. VIEW_ALGORITHM_TEMPTABLE = 1,
  7. VIEW_ALGORITHM_MERGE = 2
  8. };

使用merge算法的视图或子查询能够将查询条件下推到视图或子查询内部;而temptable算法子查询或视图不能将条件下推,只能在结果集上做进一步过滤。优化器对对这一判断标准为:

  1. bool merge_derived(THD *thd, TABLE_LIST *derived_table)
  2. {
  3. ...
  4. // Check whether derived table is mergeable, and directives allow merging
  5. if (!derived_unit->is_mergeable() ||
  6. derived_table->algorithm == VIEW_ALGORITHM_TEMPTABLE ||
  7. (!thd->optimizer_switch_flag(OPTIMIZER_SWITCH_DERIVED_MERGE) &&
  8. derived_table->algorithm != VIEW_ALGORITHM_MERGE))
  9. DBUG_RETURN(false);
  10. ...
  11. }

条件下推原则

不是所有数据库引擎都完美实现条件下推下推到子查询的功能。对MySQL中使用聚合查询的视图或者from子查询,建议的条件下推原则是:

       查询中只依赖于视图或者from子查询输出字段的where 条件能够安全的下推。

同时需要注意条件下推到视图或derived table子查询后所存放的恰当位置:

  1. 从语义上看,下推到聚合子查询的条件可以放在 HAVING 子句里。下推后的 HAVING字句可以是: HAVING xxx and NEW_CONDITION operation VALUE;
  2. 若条件是子查询的group 字段,且该条件上有索引,那么将该条件放在子查询的where字句中,性能会更好(HAVING条件中不含聚合函数时,将该条件下推到where字句中过滤整个group)。

对于其他类型的视图或from子查询,也可以通过语义检查的方式进行人工条件下推。

总结

任何数据库的优化器都不是万能的。 了解优化器的特性后并规避其短处,才能写出最优SQL语句。

MySQL · 性能优化 · 条件下推到物化表的更多相关文章

  1. MySQL · 性能优化 · MySQL常见SQL错误用法(转自-阿里云云栖社区)

    作者:阿里云云栖社区链接:https://zhuanlan.zhihu.com/p/26043916来源:知乎著作权归作者所有,转载请联系作者获得授权. 前言 MySQL在2016年仍然保持强劲的数据 ...

  2. Mysql性能优化三(分表、增量备份、还原)

    接上篇Mysql性能优化二 对表进行水平划分 如果一个表的记录数太多了,比如上千万条,而且需要经常检索,那么我们就有必要化整为零了.如果我拆成100个表,那么每个表只有10万条记录.当然这需要数据在逻 ...

  3. MySQL 性能优化系列之一 单表预处理

    MySQL 性能优化系列之一 单表预处理 背景介绍 我们经常在写多表关联的SQL时,会想到 left jion(左关联),right jion(右关联),inner jion(内关联)等. 但是,当表 ...

  4. MySQL性能优化(五):分表

    原文:MySQL性能优化(五):分表 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/vbi ...

  5. 涨姿势:Mysql 性能优化完全手册

    涨姿势:Mysql 性能优化完全手册 深入理解MySQL服务器架构 客户端层 MySQL逻辑架构整体分为三层,最上层为客户端层,诸如:连接处理.授权认证.安全等功能均在这一层处理. 中间层 MySQL ...

  6. [MySQL性能优化系列]巧用索引

    1. 普通青年的索引使用方式 假设我们有一个用户表 tb_user,内容如下: name age sex jack 22 男 rose 21 女 tom 20 男 ... ... ... 执行SQL语 ...

  7. MySQL性能优化:索引

    MySQL性能优化:索引 索引提供指向存储在表的指定列中的数据值的指针,然后根据您指定的排序顺序对这些指针排序.数据库使用索引以找到特定值,然后顺指针找到包含该值的行.这样可以使对应于表的SQL语句执 ...

  8. MySQL性能优化总结

    一.MySQL的主要适用场景 1.Web网站系统 2.日志记录系统 3.数据仓库系统 4.嵌入式系统 二.MySQL架构图: 三.MySQL存储引擎概述 1)MyISAM存储引擎 MyISAM存储引擎 ...

  9. MYSQL性能优化的最佳20+条经验

    MYSQL性能优化的最佳20+条经验 2009年11月27日 陈皓 评论 148 条评论  131,702 人阅读 今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数 ...

随机推荐

  1. jquery限制div字符长度,超出部分已“…”显示

    $(".content").each(function(){ if($(this).text().length>100){ $(this).text($(this).text ...

  2. Spring对加载的bean之间循环依赖的处理

    根据下面文档的叙述,简言之: 对于相互之间通过构造函数注入相互循环依赖的情况,Spring会抛出BeanCurrentlyInCreationException错误. 如果AB两个beans是通过属性 ...

  3. 如何"格式化"用过的磁带,让他被新磁带机重复利用

    1. 2.  套用翁磊的话: 勾上表示和之前的磁带信息进行协商(但是会失败), 不勾则表示强行擦除.

  4. RESTful和JAX-RS

    一.简介 Java Web有很多成熟的框架,主要可以分为两类Web Application和Web Services.用于Web Application的框架包括官方的Servlet/JSP, JST ...

  5. 2016.07.08,英语,《Vocabulary Builder》Unit 24

    mand/mend comes from mandare, Latin for 'entrust' or 'order'. command and commandment: [kə'mændmənt] ...

  6. Physically Based Render in Game 序

    基于物理渲的渲染理论,从SIGGRAPH06被Naty Hoffman等人提出后,近年来也越来越多的被各大游戏公司所采用,几乎已经是次世代游戏的标准特性,也是每个3D游戏工作者所必备的知识,尽管每年S ...

  7. Yii源码阅读笔记(十九)

    View中渲染view视图文件的前置和后置方法,以及渲染动态内容的方法: /** * @return string|boolean the view file currently being rend ...

  8. Java中的定时调度

    Timer类是一个线程设施,用于实现在某个时间或者某一段时间后安排某个任务执行一次或者定期重复执行.需要与TimerTask配合使用. TimerTask类用来实现由Timer安排的一次或重复执行的某 ...

  9. Java多态与C++中多态的实现

    大牛的文章,值得拜读http://www.ibm.com/developerworks/cn/java/j-lo-polymorph/ 粘贴过来好多图片丢失了 /(ㄒoㄒ)/~~ 众所周知,多态是面向 ...

  10. Apache Apex

    http://apex.apache.org/docs.html https://apex.apache.org/docs/apex/application_development/