MySQL基础 - 基于规则的优化
https://blog.csdn.net/wanghai__/article/details/6426941 看这个吧,一下就说清楚了。
https://blog.csdn.net/mashaokang1314/article/details/110480807
http://mysql.taobao.org/monthly/2016/07/08/
IN查询的话导致的问题:
子查询结果集可能太多内存都放不下
还有子查询的结构集太多,IN的参数就特别多,无法有效的使用索引,只能外层全表扫描。全表扫描,IN参数特别多判断就特别慢。
为了解决这个问题,不直接将不相关子查询的结果集当做外层查询的参数,而是将该结果集写入另一个临时表里,这就是物化:
MySQL查询优化器有多种策略可用于评估子查询:
- 对于
IN
(或=ANY
)子查询,优化器具有以下选择:- 半连接
- 物化
EXISTS
- 对于
NOT IN
(或<>ALL
)子查询,优化器具有以下选择:- 物化
EXISTS
物化表就是会把不直接将不相关子查询的结构集当作外层查询的参数,而是将该结果集写入一个临时表(关键点在于对子查询结果集去重),
该临时表的列就是子查询结果集中的列,写入临时表的记录会去重。
这个临时表就是物化表 正常会建立基于内存的临时表,会对临时表建立hash索引。如果结果集数据过多,超过了tmp_table_size(临时表上限)或者max_heap_table_size 就会转用磁盘存储该结果集,索引类型就是B+树。
物化也是有条件的:
内部和外部表达式的类型必须匹配。
内部表达式不能是BLOB。
表连接的方式如join ,semi -join ,outer-join ,anti-join ;
join or inner join 内连接 只返回能匹配的数据,
outer join 外连接,匹配不到的我也返回那半边给你null,
另一种手段 semi-join 半连接,主要是优化器对子查询in的优化思路,就是通过种种手段去重,外表扫描内表只要匹配到就返回。
半连接实现的几种方法:
1) Table pullout(子查询表上拉) 当子查询的查询列是主键或者唯一二级索引生效,因为这两者不可能重。
例如 select * from s1 where key2 in (select key2 from s2 where key3 = 'a');
可以转变为 select s1.* from s1 inner join s2 on s1.key2 = s2.key2 where s2.key3 = 'a';
2) Duplicate Weedout (消除重复)
select * from s1 where key1 in (select common_filed from s2 where key3 = 'a');
可以转变为 select s1.* from s1 inner join s2 on s1.key = s2.common_filed where s2.key3 = 'a';
同时优化器建立一个临时表存储s1的主键值 并作为临时表的主键,匹配到就把S1的主键插入这个临时表,如果插入失败就证明已经有了,丢弃。
3) LooseScan (松散扫描)
select * from s1 where key3 in (select key1 from s2 where key1 > 'a' and key1 < 'b');
在子查询中s2. key1是索引,而子查询查询列表也是key1
可以转为 select s1.* from s2 inner join s1 on s2.key1 = s1.key3 where s2.key1 > 'a' and s2.key1 < 'b')
相同的key1 只需要找到第一条二级索引 key1就去匹配s1的记录,虽然是扫描索引,但只取键值相同的第一条记录取执行操作的方式就是 松散扫描。
4) Semi-join Materialization (半连接物化)
因为物化表没有重复值,也可以直接将子查询转换为连接。
5) FirstMatch (首次匹配)
最原始的方式,先取外层一条记录去子查询表中查,能查到的话把这条记录放入结果集,继续下一条,直到外层最后一条记录。
在MySQL 8.0.16及更高版本中,任何带有EXISTS子查询的语句都应与带有IN子查询语句等效。
mysql内部使用semi-join也是有条件的,并不是带有in或exists的查询就能使用半连接:
什么情况能用上半连接呢?
1) 该子查询必须是与IN操作符组成的布尔表达式,并且外层查询的WHERE 或者 ON 子句中出现。
2) 外层查询也可以有其他搜索条件,只不过必须使用AND操作符与IN子查询的搜索条件连接起来。
3) 该子查询必须是一个单一的查询,不能由UNION 连接起来的若干个查询。
4) 该子查询不能包含group by , having语句或者聚集函数。
用不上咋整?从下面两种策略找出成本更低的!
1) 反正都物化了 IN 物化表吧。
2) 把IN转化为EXISTS子查询。这样如果inner_pexpr有索引就能用到索引啊。
outer_pexpr in ( select inner_pexpr from ... where subquery_where)
转 EXISTS (select inner_pexpr from ... where subquery_where and outer_pexpr = inner_pexpr)