MySQL阅读笔记——10.基于规则优化
MySQL优化器会依据一些规则在执行前将语句进行优化,这个过程叫 语句重写
-
移除不必要的括号
-
某列和能够确定值的变量比较,直接替换为常量
-
移除永真式和永假式
-
计算常量表达式,索引列最好单独出现在表达式
-
如果没有聚集函数以及分组条件,查询优化器会将HAVING和WHERE子句合并
-
表中只有一条记录or没有记录or使用主键或者唯一二级索引等值连接查询的表称为 常量表 ,连接查询时会先查询 常量表 替换查询条件再查询其它表
-
内连接 驱动表 和 被驱动表 位置可以互换,不符合
where
条件的记录不参与连接,如果在where
语句中将空值过滤则可以将外连接转换为内连接,从而进行优化
10.1 子查询优化(主要针对IN子句优化)
子查询必须用小括号括起来,放在from
语句后面的子查询结果集叫做 派生表,只返回单一值的查询结果叫做 标量子查询(可以通过limit 1
限制记录数,并指定返回某一列),子查询不依赖外层查询称为 不相关子查询,子查询依赖外层子查询称为 相关子查询
不相关子查询:
不相关的标量子查询或者行子查询,会单独执行子查询和外层查询,相当于两个单表连接查询,如果子查询是在IN子句则会先执行IN中子查询,再将结果集当作参数执行外层子查询
对于不相关的IN查询,如果参数特别多则不能使用index dive
,可能无法使用索引,只能对外层查询全表扫描,匹配时间也会很长。因此MySQL对子查询先尝试 半查询(semi-join),如果不能转换为 半查询 尝试 物化 之后再查询或者之间转换为 EXISTS语句再查询。
-
半查询:等值查询不关心多少条记录于外层查询匹配,只关心是否存在与子查询匹配的记录,
semi-join
只是内部执行子查询的一种方式,具体由很多种方法:-
子查询中表上拉(Table pullout):子查询结果只有 主键或唯一索引 时候,可以指接把子查询上拉到
FRPM语句
后转换为内连接 -
重复值消除(DuplicateWeedout execution strategy):转换为半连接查询,外围查询某条记录可能在子查询中对应多条记录,可以建立只有主键的临时表,每次匹配就将主键添加到该表,如果添加失败说明之前匹配过,则直接丢弃
-
松散扫描(LooseScan execution strategy):如果子查询条件和返回列值相同,那么相同的列值只取一条去与外层查询匹配
-
首次匹配(FirstMatch execution strategy):逐条读取外层记录,然后到子查询寻找匹配的记录
-
-
因此MySQL将子查询结果放到基于内存的使用Memory引擎的临时表,并且建立哈希索引,如果子查询结构集非常大,超过系统变量
tmp_table_size
和max_heap_table_size
则会使用基于磁盘的存储引擎存放子查询结果,索引类型也转换为B+树,将子查询保存到临时表的过程叫做 物化,存储子查询结果集的临时表叫 物化表 之后将子查询转换为 内连接查询 选择最优执行方案
由于相关子查询不是一个独立查询,不能在分析外围查询之前就 物化 子查询。 如果
IN
子查询不满足转换为semi-join
的条件,又不能转换为物化表或者转换为物化表的成本太大,那么它就会被转换为EXISTS
查询。
-
转换为
EXISTS
查询:EXISTS
语句范围true
或false
,但是IN子句中有NULL则返回NULL,而一般in
子句放在where
或者on
子句中,而where
或者on
子句不区分NULL
和FALSE
用文字总结一下,只有符合下边这些条件的子查询才可以被转换为semi-join
:
-
该子查询必须是和
IN
语句组成的布尔表达式,并且在外层查询的WHERE
或者ON
子句中出现。 -
外层查询也可以有其他的搜索条件,只不过和
IN
子查询的搜索条件必须使用AND
连接起来。 -
该子查询必须是一个单一的查询,不能是由若干查询由
UNION
连接起来的形式。 -
该子查询不能包含
GROUP BY
或者HAVING
语句或者聚集函数。
相关子查询:
如果相关子查询再IN子句,意味着子查询依赖外查询的值,因此逐条从外查询取出记录然后去匹配执行内查询,相关子查询转换为EXIST语句后可能用到索引,提升效率
# 相关子查询
select * from s1 where key1 in (select key3 from s2 where s1.common_field = s2.common_field)
# 转换为 EXISTS语句
select * from s1 where exists (select 1 from s2 where s1.common_field = s2.common_field and s1.key1=s2.key3)
将 子查询 放到外层查询的
from
后面,那么子查询的结果相当于一个 派生表,派生表有两种执行策略:首先尝试 和外层查询合并 ,如果不行,则会将 派生表物化 再进行查询