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_sizemax_heap_table_size则会使用基于磁盘的存储引擎存放子查询结果,索引类型也转换为B+树,将子查询保存到临时表的过程叫做 物化,存储子查询结果集的临时表叫 物化表 之后将子查询转换为 内连接查询 选择最优执行方案

由于相关子查询不是一个独立查询,不能在分析外围查询之前就 物化 子查询。 如果IN子查询不满足转换为semi-join的条件,又不能转换为物化表或者转换为物化表的成本太大,那么它就会被转换为EXISTS查询。

  • 转换为EXISTS查询:EXISTS语句范围truefalse,但是IN子句中有NULL则返回NULL,而一般in子句放在where或者on子句中,而where或者on子句不区分NULLFALSE

用文字总结一下,只有符合下边这些条件的子查询才可以被转换为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后面,那么子查询的结果相当于一个 派生表,派生表有两种执行策略:首先尝试 和外层查询合并 ,如果不行,则会将 派生表物化 再进行查询


posted @ 2020-10-08 23:46  摩诃、  阅读(152)  评论(0编辑  收藏  举报