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

背景

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

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

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

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

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

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

分析

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

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

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

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算法。 比如:

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

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

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

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

bool merge_derived(THD *thd, TABLE_LIST *derived_table)
{
...
  // Check whether derived table is mergeable, and directives allow merging
  if (!derived_unit->is_mergeable() ||
      derived_table->algorithm == VIEW_ALGORITHM_TEMPTABLE ||
      (!thd->optimizer_switch_flag(OPTIMIZER_SWITCH_DERIVED_MERGE) &&
       derived_table->algorithm != VIEW_ALGORITHM_MERGE))
    DBUG_RETURN(false);
...
}

条件下推原则

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

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

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

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

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

总结

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

时间: 2024-10-03 06:10:11

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

MySQL · 性能优化 · SQL错误用法详解

前言 MySQL在2016年仍然保持强劲的数据库流行度增长趋势.越来越多的客户将自己的应用建立在MySQL数据库之上,甚至是从Oracle迁移到MySQL上来.但也存在部分客户在使用MySQL数据库的过程中遇到一些比如响应时间慢,CPU打满等情况.阿里云RDS专家服务团队帮助云上客户解决过很多紧急问题.现将<ApsaraDB专家诊断报告>中出现的部分常见SQL问题总结如下,供大家参考. 常见SQL错误用法 1. LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如

MySQL · 性能优化 · MySQL常见SQL错误用法

前言 MySQL在2016年仍然保持强劲的数据库流行度增长趋势.越来越多的客户将自己的应用建立在MySQL数据库之上,甚至是从Oracle迁移到MySQL上来.但也存在部分客户在使用MySQL数据库的过程中遇到一些比如响应时间慢,CPU打满等情况.阿里云RDS专家服务团队帮助云上客户解决过很多紧急问题.现将<ApsaraDB专家诊断报告>中出现的部分常见SQL问题总结如下,供大家参考. 常见SQL错误用法 1. LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如

MySQL性能优化总结(转)

一.MySQL的主要适用场景 1.Web网站系统 2.日志记录系统 3.数据仓库系统 4.嵌入式系统 二.MySQL架构图:   三.MySQL存储引擎概述 1)MyISAM存储引擎 MyISAM存储引擎的表在数据库中,每一个表都被存放为三个以表名命名的物理文件.首先肯定会有任何存储引擎都不可缺少的存放表结构定义信息的.frm文件,另外还有.MYD和.MYI文件,分别存放了表的数据(.MYD)和索引数据(.MYI).每个表都有且仅有这样三个文件做为MyISAM存储类型的表的存储,也就是说不管这个

mysql性能优化之索引优化_Mysql

作为免费又高效的数据库,mysql基本是首选.良好的安全连接,自带查询解析.sql语句优化,使用读写锁(细化到行).事物隔离和多版本并发控制提高并发,完备的事务日志记录,强大的存储引擎提供高效查询(表记录可达百万级),如果是InnoDB,还可在崩溃后进行完整的恢复,优点非常多.即使有这么多优点,仍依赖人去做点优化,看书后写个总结巩固下,有错请指正. 完整的mysql优化需要很深的功底,大公司甚至有专门写mysql内核的,sql优化攻城狮,mysql服务器的优化,各种参数常量设定,查询语句优化,主

详解MySQL性能优化(一)_Mysql

一.MySQL的主要适用场景1.Web网站系统 2.日志记录系统 3.数据仓库系统 4.嵌入式系统 二.MySQL架构图:   三.MySQL存储引擎概述 1)MyISAM存储引擎 MyISAM存储引擎的表在数据库中,每一个表都被存放为三个以表名命名的物理文件.首先肯定会有任何存储引擎都不可缺少的存放表结构定义信息的.frm文件,另外还有.MYD和.MYI文件,分别存放了表的数据(.MYD)和索引数据(.MYI).每个表都有且仅有这样三个文件做为MyISAM存储类型的表的存储,也就是说不管这个表

19个MySQL性能优化要点解析_Mysql

以下就是跟大家分享的19个MySQL性能优化主要要点,一起学习学习. 1.为查询优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被MySQL的数据库引擎处理的.当有很多相同的查询被执行了多次的时候,这些查询结果会被放到一个缓存中,这样,后续的相同的查询就不用操作表而直接访问缓存结果了. 这里最主要的问题是,对于程序员来说,这个事情是很容易被忽略的.因为,我们某些查询语句会让MySQL不使用缓存.请看下面的示例: // 查询缓存不开启 $r = mysq

Mysql性能优化案例研究-覆盖索引和SQL_NO_CACHE_Mysql

场景 产品中有一张图片表pics,数据量将近100万条,有一条相关的查询语句,由于执行频次较高,想针对此语句进行优化 表结构很简单,主要字段: 复制代码 代码如下: user_id 用户ID picname 图片名称 smallimg 小图名称 一个用户会有多条图片记录,现在有一个根据user_id建立的索引:uid,查询语句也很简单:取得某用户的图片集合: 复制代码 代码如下: select picname, smallimg from pics where user_id = xxx; 优化

MySQL性能优化的最佳20+条经验_Mysql

今天,数据库的操作越来越成为整个应用的性能瓶颈了,这点对于Web应用尤其明显.关于数据库的性能,这并不只是DBA才需要担心的事,而这更是我们程序员需要去关注的事情.当我们去设计数据库表结构,对操作数据库时(尤其是查表时的SQL语句),我们都需要注意数据操作的性能.这里,我们不会讲过多的SQL语句的优化,而只是针对MySQL这一Web应用最多的数据库.希望下面的这些优化技巧对你有用. 1. 为查询缓存优化你的查询 大多数的MySQL服务器都开启了查询缓存.这是提高性最有效的方法之一,而且这是被My

mysql性能优化-简易版

mysql性能优化 sql语句优化 如何发现有问题的sql? 开启mysql慢查询 show variables like 'slow_query_log' set global slow_query_log_file='/var/mysql/mysql_log/mysql-slow.log' set global log_queries_not_using_index=on; set global long_query_time=1 MySQL慢查日志分析工具之mysqldumpslow my