Mysql优化(出自官方文档) - 第六篇
Mysql优化(出自官方文档) - 第六篇
Optimizing Subqueries, Derived Tables, View References, and Common Table Expressions
对于子查询,Mysql通常使用如下的优化方式:
- 对于
IN(or =ANY)式的子查询,优化器使用如下方式:semijoin- 物化
EXISTS策略
- 对于
NOT IN(OR <>ALL)式的子查询,优化器使用如下方式:- 物化
EXISTS策略
对于derived tables,优化器使用如下方式(对于view references和common table expressions也同样适用):
- 将
derived table合并到外部查询里 - 将
derived table物化为临时表。
特别的,对于带有子查询的UPDATE 和 DELETE语句,优化方式为:
优化器不会使用
semijoin和物化来优化这种情况,而是将其重写为多张表的UPDATE和DELETE操作,同时使用join来代替子查询操作。
1 Optimizing IN and EXISTS Subquery predicates with Semijoin Transformations
假设有两张表,class和roster,现在要查询有学生出勤课程的课程编号和课程名称,我们可以很简单的写出下面的语句:
SELECT class.class_num, class.class_name
FROM class INNER JOIN roster
WHERE class.class_num = roster.class_num;
假设class_num是class表的primary key,可以看出来,上面的查询结果中必然有重复列,因为多个学生可以出勤同一个课程,所以,为了去重,我们可以加上SELECT DISTINCT这样的限定。
除此之外,还可以将上面的join语句改为子查询的方式,如下:
SELECT class_num, class_name
FROM class
WHERE class_num IN (SELECT class_num FROM roster);
该语句有如下特点:
SELECT的目标只有一张表的列IN表示在第二张表中只要有第一张表相同的值就立即返回
此时,Mysql会将上面的子查询优化为semijoin,semijoin的特点就是在join中一旦查询到匹配行,就立即只返回一张表的数据,后续重复的值将没有必要继续扫描。
同时,在Mysql8.0.17之后,下面的语句也会被转换为antijoin(和semijoin相反,当第一张表在第二张表中没有匹配行时,立即返回第一张表的列。)
NOT IN (SELECT ... FROM ...)NOT EXISTS (SELECT ... FROM ...).IN (SELECT ... FROM ...) IS NOT TRUEEXISTS (SELECT ... FROM ...) IS NOT TRUE.IN (SELECT ... FROM ...) IS FALSEEXISTS (SELECT ... FROM ...) IS FALSE.
对于semijoin,Mysql主要的处理方式如下:
Duplicate WeedoutFirstMatchLooseScanMaterialize
这四种的实现方式网上均有介绍,这里就不赘述了。
2 Optimizing Subqueries with Materialization
Mysql经常使用物化的方式来优化subquery,通常的方式是创建一个临时表(一般来讲是全内存临时表,只有当临时表变得比较大的时候,才会进行下盘处理),并且优化器会使用hash index的方式对临时表创建索引来加快查询,index的值是唯一的,所以能够避免重复的值。
对于下面的语句,如果不适用物化的话:
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
优化器会将该语句重写为:
SELECT * FROM t1
WHERE EXISTS (SELECT t2.b FROM t2 WHERE where_condition AND t1.a=t2.b);
这种有所关联的子查询语句(关联指子查询语句中不仅查询t2内表的数据,还会和t1外表有关), 这种类型的子查询的执行过程为:外表每执行一次,子查询就要执行一次,所以效率很低。
为了让Mysql使用物化来运行子查询,查询语句必须符合如下形式:
外查询中
oe_i和内查询ie_i不能为null,N是1或者更大的值(??为什么?不是很理解?)(oe_1, oe_2, ..., oe_N) [NOT] IN (SELECT ie_1, i_2, ..., ie_N ...)
外查询和内查询均只有一个表达式,表达式的值可以为
nulloe [NOT] IN (SELECT ie ...)
谓词必须为
in或者not in,或者和FALSE具有相同语义的表达式。
举例如下,下面的语句将会使用物化技术:
SELECT * FROM t1
WHERE t1.a IN (SELECT t2.b FROM t2 WHERE where_condition);
下面的语句将无法使用物化技术,因为t2.b可能为null:
SELECT * FROM t1
WHERE (t1.a,t1.b) NOT IN (SELECT t2.a,t2.b FROM t2
WHERE where_condition);
需要注意的是,对于列的类型信息,必须满足如下条件,才能使用物化技术:
- 内查询和外查询的列必须匹配,比如如果一个
integer另外一个是decimal,优化器将无法使用物化技术。 - 内查询的表达式类型不能为
BLOB,根据第一条的限制,外查询也同样不能为BLOB
在EXPLAIN的输出里面,如果使用了物化技术,那么输出如下:
select_type将由DEPENDENT SUBQUERY变为SUBQUERY, DEPENDENT的意思是外查询每执行一次,内查询就要执行一次,使用物化技术的话,内查询只需要执行一次。- 在
EXPLAIN的输出里面,SHOW WARNINGS会包含materialize和materialized-subquery。
3 Optimizing Subqueries with the EXISTS Strategy
对于如下的语句,如果不采取章节2中的优化方式,那么通常的执行方式是:外查询执行一次,内查询在执行一次。
outer_expr IN (SELECT inner_expr FROM ... WHERE subquery_where)
对于这种情况,由于只需要特定列,所以Mysql通常会使用条件下推的方式进行优化,优化后的结果为:
EXISTS (SELECT 1 FROM ... WHERE subquery_where AND outer_expr=inner_expr)
这样子,子查询的条件将更加严格,可以大大降低子查询需要行数。
同理,如果选取的是多列,那么也可以采用同样的优化方式:
(oe_1, ..., oe_N) IN
(SELECT ie_1, ..., ie_N FROM ... WHERE subquery_where)
上面这条语句将会优化为:
EXISTS (SELECT 1 FROM ... WHERE subquery_where
AND oe_1 = ie_1
AND ...
AND oe_N = ie_N)
这种条件下推的方式有其局限性,如下:
outer_expr和inner_expr均不能为NULL如果
OR或者AND语句在WHERE语句中,Mysql假设用户并不关心返回的值是NULL还是FALSE,因此,对于下面的语句:... WHERE outer_expr IN (subquery)
无论子查询返回
NULL还是返回FALSE,WHERE都不会接受。
如果说,上面两条限制都不符合,那么此时的优化方式将会变的很复杂,主要分为以下两种情况:
如果
outer_expr不会有NULL产生,此时,outer_expr IN (SELECT ...)的结果分为以下两种情况:- 如果
SELECT在inner_expr is NULL的条件下返回了任意行,那么结果为NULL - 如果
SELECT只返回了非NULL行或者说没有返回任何一行,那么结果为FALSE
对于这种情况,当查找
outer_expr = inner_expr时,如果没有找到,还需要查找inner_expr is NULL这样的列,因此,这种语句会被转换为下面的形式:EXISTS (SELECT 1 FROM ... WHERE subquery_where AND
(outer_expr=inner_expr OR inner_expr IS NULL))
在
EXPLAIN里面,这样的语句type列为:ref_or_null- 如果
如果
outer_expr有可能产生NULL列,那么情况将会变得比较复杂,对于NULL IN (SELECT inner_expr ...)这样的语句,结果分为两种情况:- 如果
SELECT返回了任意行,那么结果为NULL - 如果
SELECT没有返回行,那么结果为FALSE
所以,优化器为了加快速度,需要分两种情况来处理:
如果
outer_expr的结果为NULL,就需要判断子查询是否返回任意行,此时无法使用条件下推的优化,这个时候的性能是最差的,外查询没查询一次,就需要执行一次子查询。如果
outer_expr的结果不为NULL,那么就可以使用上面提到的条件下推这种优化方式,语句将会被重写为:EXISTS (SELECT 1 FROM ... WHERE subquery_where AND outer_expr=inner_expr)
综上,为了包含上面两种情况,Mysql使用一种叫做"
trigger"的函数(不同于数据库里面创建的trigger),在Mysql里面体现为Item_func_trig_cond类,因此,上面的两种情况都会统一被转换为:EXISTS (SELECT 1 FROM ... WHERE subquery_where
AND trigcond(outer_expr=inner_expr))
同理,如果有多列,如下面的语句:
(oe_1, ..., oe_N) IN (SELECT ie_1, ..., ie_N FROM ... WHERE subquery_where)
将会被转换为:
EXISTS (SELECT 1 FROM ... WHERE subquery_where
AND trigcond(oe_1=ie_1)
AND ...
AND trigcond(oe_N=ie_N)
)
对于
trigcond(x),Mysql的处理方式如下:- 如果外查询的结果不是
NULL,那么trigcond的结果即为x的结果 - 如果外查询的结果为
NULL,那么trigcond返回TRUE(这里不是很理解,需要详细理解下)
Note
这里的
trigger不同于平时使用sql创建的trigger``,CREATE TRIGGER- 如果外查询的结果不是
- 如果
帮助优化器进行优化的一些技巧:
如果某一列永远不会产生
NULL列,那么将其声明为NOT NULL,这样子可以帮助优化器进行更进一步的优化。如果不需要区分
NULL和FALSE,对于下面的语句:outer_expr IN (SELECT inner_expr FROM ...)
为了避免Mysql采用最糟糕的方式进行执行,可以将该语句修改为:
(outer_expr IS NOT NULL) AND (outer_expr IN (SELECT inner_expr FROM ...))
这样子Mysql可以使用短路判断尽快返回结果,可以大量的减少
AND后面语句的执行次数。另外,也可以写成下面这样子:
EXISTS (SELECT inner_expr FROM ...
WHERE inner_expr=outer_expr)
4 Optimizing Derived Tables, View References, and Common Table Expressions with Merging or Materialization
在优化derived table的时候,通常采用两种策略(同样适用于view references和common table expressions):
- 将
derived table合并到外层的查询里面 - 物化
derived table到一个临时表里面
举例如下:
SELECT * FROM (SELECT * FROM t1) AS derived_t1;
会被优化为:
SELECT * FROM t1;
下面的语句:
SELECT *
FROM t1 JOIN (SELECT t2.f1 FROM t2) AS derived_t2 ON t1.f2=derived_t2.f1
WHERE t1.f1 > 0;
会被优化为:
SELECT t1.*, t2.f1
FROM t1 JOIN t2 ON t1.f2=t2.f1
WHERE t1.f1 > 0;
可以明显的看到,如果采取非物化的方式,执行效率将会大大提升。
优化器会将derived table里面的ORDER BY优化到外层查询语句中,但是,必须满足如下条件:
- 外层查询没有使用
group by或者聚合函数 - 外层查询没有使用
DISTINCT,HAVING, orORDER BY - 外层查询只有在
FROM中才使用到了derived table
如果优化器没办法使用MERGE,意味着只能使用物化为临时表的方式来执行,此时,为了加快效率,将采用如下的优化方式:
- 优化器只有在需要
derived table的时候才会进行物化操作,这样子有时候就可以避免进行物化操作。比如说:一个查询中子查询需要进行物化操作,条件里面有外表和dervied table的对比,此时先执行外查询,如果外查询返回了空行,这个时候,dervied table就没必要再继续执行了,可以减少没有必要的物化操作。 - 在执行期间,优化器会根据需要给
dervied table添加对应的索引,这样子可以提高dervied table的访问效率。
Mysql优化(出自官方文档) - 第六篇的更多相关文章
- Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇)
目录 Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇) 1 Optimizing Data Size 2 Optimizing MySQL Data Types 3 Optimizing ...
- Mysql优化(出自官方文档) - 第二篇
Mysql优化(出自官方文档) - 第二篇 目录 Mysql优化(出自官方文档) - 第二篇 1 关于Nested Loop Join的相关知识 1.1 相关概念和算法 1.2 一些优化 1 关于Ne ...
- Mysql优化(出自官方文档) - 第一篇(SQL优化系列)
Mysql优化(出自官方文档) - 第一篇 目录 Mysql优化(出自官方文档) - 第一篇 1 WHERE Clause Optimization 2 Range Optimization Skip ...
- Mysql优化(出自官方文档) - 第三篇
目录 Mysql优化(出自官方文档) - 第三篇 1 Multi-Range Read Optimization(MRR) 2 Block Nested-Loop(BNL) and Batched K ...
- Mysql优化(出自官方文档) - 第五篇
目录 Mysql优化(出自官方文档) - 第五篇 1 GROUP BY Optimization 2 DISTINCT Optimization 3 LIMIT Query Optimization ...
- Mysql优化(出自官方文档) - 第八篇(索引优化系列)
目录 Mysql优化(出自官方文档) - 第八篇(索引优化系列) Optimization and Indexes 1 Foreign Key Optimization 2 Column Indexe ...
- Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇)
Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 目录 Mysql优化(出自官方文档) - 第十二篇(优化锁操作篇) 1 Internal Locking Methods Row-Leve ...
- Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇)
Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇) 目录 Mysql优化(出自官方文档) - 第十篇(优化InnoDB表篇) 1 Optimizing Storage Layout f ...
- Mysql优化(出自官方文档) - 第七篇
Mysql优化(出自官方文档) - 第七篇 目录 Mysql优化(出自官方文档) - 第七篇 Optimizing Data Change Statements 1 Optimizing INSERT ...
随机推荐
- JavaI/O流汇总
Java中常见流学习汇总 流的含义 流在Java中是指计算中流动的缓冲区. 从外部设备流向中央处理器的数据流成为"输入流",反之成为"输出流". 字符流和字节流 ...
- 巧用SQL拼接语句
前言: 在日常数据库运维过程中,可能经常会用到各种拼接语句,巧用拼接SQL可以让我们的工作方便很多,达到事半功倍的效果.本篇文章将会分享几个日常会用到的SQL拼接案例,类似的SQL还可以举一反三,探索 ...
- C++ primer plus读书笔记——第8章 函数探幽
第8章 函数探幽 1. 对于内联函数,编译器将使用相应的函数代码替换函数调用,程序无需跳到一个位置执行代码,再调回来.因此,内联函数的运行速度比常规函数稍快,但代价是需要占用更多内存. 2. 要使用内 ...
- leecode之Implement strStr()
KMP算法的实现: #include <stdio.h> #include <string.h> #include <stdlib.h> int strStr(ch ...
- 3.下载CentOS镜像
下载CentOS镜像 引子: 镜像可以看成是类似ZIP的压缩文件,与rar ZIP压缩包类似,镜像文件是无法直接使用的,需要利用一些虚拟光驱工具 进行解压后才能使用 我们这里就是CentOS系统的镜像 ...
- 《Spring 手撸专栏》第 3 章:初显身手,运用设计模式,实现 Bean 的定义、注册、获取
作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 你是否能预见复杂内容的设计问题? 讲道理,无论产品功能是否复杂,都有很大一部分程序员 ...
- C#基础之==(双等于号)与equals()区别
C#中Equals和= =比较 这两种方式也是大家在日常编码工作当中用的比较多的判断方式.之前在使用的时候也没太关注两者在比较不同类型的时候存在哪些区别. 今天就和大家一起深入了解一下其中区别 一.值 ...
- 使用nuget包下载Entity Framework6.0无法使用模型类与数据库上下文自动生成controller与view
解决方法:卸载掉原有的6.0版本EF,从控制台安装5.0版本的. >工具>库程序包管理器>程序包管理器控制台.在PM>后面输入安装命令. 命令如下 Install-Packag ...
- 三大主流开源硬件对比:Arduino vs Raspberry Pi vs BeagleBone
http://www.elecfans.com/emb/361236_3.html 下文摘自上面的链接 软硬件整合是今年一再被提及的话题,如今我们也可以看到不少硬件创业的成功案例,比如Jawbone ...
- 推荐:C#命名规范12条
编码规范对于程序员而言尤为重要,有以下几个原因: 1.一个项目的生命周期中,80%的花费在于维护; 2.几乎没有任何一个项目,在其整个生命周期中,均由最初的开发人员来维护; 3.命名规范可以改善项目的 ...