Mysql优化(出自官方文档) - 第二篇
Mysql优化(出自官方文档) - 第二篇
1 关于Nested Loop Join的相关知识
1.1 相关概念和算法
Mysql在实现join的时候,采用的Nested Loop Join
技术,join的方式还有其他两种:Hash Join
和SortMergeJoin
,Mysql处于逻辑实现统一的角度,只实现了Nested Loop Join
,假设有t1, t2, t3三张表,EXPLAIN
优化的结果为:
Table Join Type
t1 range
t2 ref
t3 ALL
那么其Join的方式可以用下面的为代表来表示:
for each row in t1 matching range {
for each row in t2 matching reference key {
for each row in t3 {
if row satisfies join conditions, send to client
}
}
}
为了减少内表的IO,Mysql又引入了NestedLoopJoin
的一个变种,叫做:Block Nested-Loop Join
,简称为BNL
,简单理解就是NestedLoopJoin With Buffer
,为每一个join维护一个Join Buffer
,对于外层循环,每次将扫描的行放入到Buffer中,内表直接对Buffer中的行进行匹配操作,对应的伪代码如下:
for each row in t1 matching range {
for each row in t2 matching reference key {
store used columns from t1, t2 in join buffer
if buffer is full {
for each row in t3 {
for each t1, t2 combination in join buffer {
if row satisfies join conditions, send to client
}
}
empty join buffer
}
}
}
if buffer is not empty {
for each row in t3 {
for each t1, t2 combination in join buffer {
if row satisfies join conditions, send to client
}
}
}
1.2 一些优化
在Join语句中,除了
on
语句后面的条件外,如果还存在where
语句,假设有三张表t1, t2, t3
,如下面的格式:select * from (t1,t2) on P1(t1,t2) inner join t3 on P2(t2,t3) where C1(t1) and C2(t2) and C3(t3);
其
inner join
的伪代码如下所示:FOR each row t1 in T1 such that C1(t1) {
FOR each row t2 in T2 such that P1(t1,t2) AND C2(t2) {
FOR each row t3 in T3 such that P2(t2,t3) AND C3(t3) {
IF P(t1,t2,t3) {
t:=t1||t2||t3; OUTPUT t;
}
}
}
}
Mysql会采用条件下推的优化方式,提前将
where
的条件限制在inner join
的循环里面,这样子,如果C1
的条件非常严苛,那么可以避免大量对t2, t3
表的IO操作。需要注意的是:这种优化方式可能并不适用于outer join
,outer join
中where
提前会导致不同的结果。outer join
优化对于
left join
,如果对于generated NULL row
, 后面的where
条件始终为false
, 那么,这个left join
可以被安全的转换为inner join
,如下面的语句所示,假设t1.column1
始终为NULL
;SELECT * FROM t1 LEFT JOIN t2 ON (column1) WHERE t2.column2=5;
那么这条语句可被转换为
inner join
:SELECT * FROM t1, t2 WHERE t2.column2=5 AND t1.column1=t2.column1;
解释:之所以会有这样的转换, 是因为这样做
t2
便不需要进行全表扫描,只需要扫描出column2 = 5
的行。有时候,在
prepare
阶段,一些琐碎的条件可以直接被移除掉(特指Mysql8.0.14+的版本),而不是在优化器里面在进行移除,提前移除这些条件,可以让优化器尽可能的把left join
转换为inner join
,比如以下语句:SELECT * FROM t1 LEFT JOIN t2 ON condition_1 WHERE condition_2 OR 0 = 1
0 = 1始终为false,因此可以直接移除掉,移除后的结果:
SELECT * FROM t1 LEFT JOIN t2 ON condition_1 where condition_2
此时,优化器便可以根据实际情况将该语句优化为
inner join
(假设按照上一个规则优化):SELECT * FROM t1 JOIN t2 WHERE condition_1 AND condition_2
outer join
一些简化措施(重写或者转换)大多数情况下,
right join
会在解析阶段直接被转换为left join
,如下所示:(T1, ...) RIGHT JOIN (T2, ...) ON P(T1, ..., T2, ...)
转换为的结果为:
(T2, ...) LEFT JOIN (T1, ...) ON P(T1, ..., T2, ...)
有时候,当
join
带有where
条件时,并且where
条件里面首先对inner table
进行访问,那么这个时候,优化器将很难对其进行优化,比如下面的例子:SELECT * T1 LEFT JOIN T2 ON P1(T1,T2)
WHERE P(T1,T2) AND R(T2)
此时,Mysql会对
where
条件进行null-rejected
判断,如果判断成功,那么outer join
就可以安全的被转换为inner join
,需要注意的是,进行null-rejected
判断的对象必须在内表上,原理很简单,因为对于outer join
,如果内表没有匹配到的行,会进行null-complemented
(对应的内表所有行被设置为NULL
),所以,如果此时where
条件对于内表的判断始终为false
,那么null-complemented
的行将会被过滤掉,此时``outer join
的结果将等价于inner join
的结果,对于下面的例子:T1 LEFT JOIN T2 ON T1.A=T2.A
null-rejected
的条件包括(t2
的列会被设置为NULL
):T2.B IS NOT NULL
T2.B > 3
T2.C <= T1.C
T2.B < 2 OR T2.C > 1
下面的条件不属于
null-rejected
,因为下面的条件对于null-complemented
行可能为真:T2.B IS NULL
T1.B < 3 OR T2.B IS NOT NULL
T1.B < 3 OR T2.B > 3
举例说明,比如下面的语句:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
LEFT JOIN T3 ON T3.B=T1.B
WHERE T3.C > 0
可以判断出,
T3.C > 0
是一个典型的null-rejected
条件,因此该语句可以被直接的转换为:SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
INNER JOIN T3 ON T3.B=T1.B
WHERE T3.C > 0
再来看一个更加复杂的例子:
SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
LEFT JOIN T3 ON T3.B=T2.B
WHERE T3.C > 0
与第一个例子唯一的趋避额就是
T2
和T3 join
的条件不同,此时join
的对象变成了T3.B = T2.B
,类似于第一个例子,上述语句可以直接转换为:SELECT * FROM T1 LEFT JOIN T2 ON T2.A=T1.A
INNER JOIN T3 ON T3.B=T2.B
WHERE T3.C > 0
在Mysql中,
inner join
是cross join
带on的版本,上面这条语句和下面是等价的:SELECT * FROM (T1 LEFT JOIN T2 ON T2.A=T1.A), T3
WHERE T3.C > 0 AND T3.B=T2.B
对于最外层的
T1
和T2
,可以看到where
条件里面的T3.B = T2.B
又是一个null-rejected
条件,因此,外层的join
也可以被直接优化为:SELECT * FROM (T1 INNER JOIN T2 ON T2.A=T1.A), T3
WHERE T3.C > 0 AND T3.B=T2.B
经过上面的优化过程,最终的语句已经没有了
outer join
,可以大幅度提高join
的效率,因此根据该优化规则,我们写join
的时候,也可以有效的利用这种优化方式。
Mysql优化(出自官方文档) - 第二篇的更多相关文章
- 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优化(出自官方文档) - 第六篇 目录 Mysql优化(出自官方文档) - 第六篇 Optimizing Subqueries, Derived Tables, View Reference ...
- Mysql优化(出自官方文档) - 第一篇(SQL优化系列)
Mysql优化(出自官方文档) - 第一篇 目录 Mysql优化(出自官方文档) - 第一篇 1 WHERE Clause Optimization 2 Range Optimization Skip ...
- Mysql优化(出自官方文档) - 第八篇(索引优化系列)
目录 Mysql优化(出自官方文档) - 第八篇(索引优化系列) Optimization and Indexes 1 Foreign Key Optimization 2 Column Indexe ...
- Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇)
目录 Mysql优化(出自官方文档) - 第九篇(优化数据库结构篇) 1 Optimizing Data Size 2 Optimizing MySQL Data Types 3 Optimizing ...
- 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 ...
随机推荐
- c++中new[ ]与delete[ ]的分析
前言 以前对c++的new[]的了解就是开辟一块内存,直到我最近在程序中用到它才发现我的了解太浅. 问题分析 new[]得到的内存空间不会自动初始化 new[]是在堆区中动态分配指定大小的内存,但是这 ...
- Deepin深度应用商店和系统更新不正常的解决方法
Deepin深度应用商店和系统更新不正常的解决方法 2020-02-04 10:25:09作者:i8520稿源:深度站 如果你的Deepin深度应用商店和系统更新不正常,可采用以下方法来解决问题. 解 ...
- 安装SpecCPU2006 on Linux of CentOS6.3, gcc4.4.7
安装SpecCPU2006 on Linux of CentOS6.3, gcc4.4.7 由于在tools/bin目录中只有ia64-linux,所以在直接运行./install.sh脚本时,系统会 ...
- Linux_网络基础管理
一.网卡的命名 1.传统网卡命名 eth0.eth1.eth2.eth3......... wlan0.wlan1.waln2.wlan3......... 2.RHEL7命名机制 systemd对网 ...
- 9.13-15 runlevel & init & service
runlevel:输出当前运行级别 runlevel命令用于输出当前Linux系统的运行级别. -quiet 不输出结果,用于通过返回值判断的场合 [root@cs6 ~]# runlevel N 3 ...
- Swift系列九 - 属性
任何一门语言都有属性的概念.Swift中的属性是怎么的呢? 一.属性 Swift中跟实例相关的属性可以分为2大类:存储属性和计算属性. 1.1. 存储属性(Stored Property) 特点: 类 ...
- 利用TortoiseGit向Github上传文件
利用TortoiseGit向Github上传文件 第一步:建一个新文件夹,作为本地仓库 第二步:右键选择设置为版本库 若弹出,确认即可 重新打开改文件,会发现多了一个绿色的小勾 在文件夹中会自动生成一 ...
- 我的Java资料小栈-START
我的Java资料小栈 前言 在学习Java的这一路中,其实说句实话,自己还是看了很多培训结构出的Java资料,有时候个人觉得培训结构有的东西还是讲的比较通俗易懂的,又想着有些最新的或者个人有时候需要及 ...
- newbee-mall开源项目被慕课网拿去做课程,然后我毫不知情,这又是什么骚操作?
万万没想到,这种事情会发生在我身上. 之前写过<开源囧事>系列而且已经写了四篇,四次开源囧事如下: <开源囧事(一)捅娄子了,写个bug被国家信息安全漏洞共享平台抓到了?> & ...
- Ubuntu 20.04 Docker 安装并配置
前言 Docker 的使用能极大地方便我们的开发,减少环境搭建,依赖安装等繁琐且容易出错的问题. 安装 Docker Ubuntu 20.04 官方 apt 源中就有 Docker,我们可以直接通过 ...