设为首页 收藏本站
查看: 641|回复: 0

[经验分享] MYSQL 优化器预处理阶段对子查询的优化

[复制链接]

尚未签到

发表于 2016-10-18 08:19:31 | 显示全部楼层 |阅读模式
 
最近看了下MYSQL优化器预处理阶段对子查询的优化, 同时还有些疑问发出来向大家请教下。
sql_resolver.cc
 
JOIN::prepare() 预处理阶段
1) remove_redundant_subquery_clauses()方法 去除子查询中冗余语句
  MySQL 中的子查询类型:enum subs_type {UNKNOWN_SUBS, SINGLEROW_SUBS, EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS};
  MySQL 会对EXISTS_SUBS, IN_SUBS, ALL_SUBS, ANY_SUBS 这四种类型的子查询过滤冗余语句,规则如下:
  a) 如果有order by 直接去掉, 因为这里排序是没有意义的 例如:
   select * from t1 where name in ( select name from t2 where t2.col = xxx order by name) -->
   select * from t1 where name in ( select name from t2 where t2.col = xxx ) 
  b) 如果有distinct 直接去掉,其实这里有个疑问:
    如果子查询有100W条记录,去重后只剩下100条记录了这样比较100条记录,否则 id > all 的话需要比较100w条记录呢,这是否说明distinct还是有效果的呢?
    例如:
   select * from t1 where id > all ( select distinct(name) from t2 where t2.col = xxx ) -->
   select * from t1 where id > all ( select name from t2 where t2.col = xxx ) 
  c) 如果子查询有group by且不包含聚合函数和having的条件 直接去掉。
    为何要加上两个非条件呢?
    聚合函数说明需要输出统计信息所以group by是有用的,having使用过滤分组的同样这样的group by也是有用的,所以这这种情况的group by需要保留。
   例如:
   select * from t1 where id in ( select id from t2  where t2.col = xxx group by t2.name) -- >
   select * from t1 where id in ( select id from t2  where t2.col = xxx )
 
2) resovle_subquery()
a)转化子查询到半连接。
  优点:子查询中全表扫描外层表的每条记录和子查询做join(5.6之前),半连接(上拉/扁平化)将子查询的表上拉到外面做join操作
  这样可以充分使用join的优化策略。
    注意 下面的这个例子还有个对外表去重操作,至于如何去重取决于优化器的半连接优化策略。
     1. 重复剔除:join结果到临时表但是在join列上加上主键,这样临时表中外层表就不会有重复的记录了  
     2. 松散扫描:子查询结果有序且子查询的join列有索引,则对子查询做分组且join每组的第一条记录
     3. 第一次匹配:外层表join到子查询的第一条符合记录就退出这条记录的join操作,接着做外层表的下一条记录join子查询
     4. 索引式物化:子查询物化到临时表,(MySQL自动为临时表创建索引),可以把临时表当成内表利用临时的索引来做join。
     5. 扫描式物化:子查询物化到临时表,但是子查询小结果集的临时表被当成了外表,所以做全表扫描。
  例如:
   select t1.* from t1 where t1.xxx in ( select t2.xxx from t2 )  -->
   select t1.* from t1 join t2 on t1.xxx = t2.xxx
  前置条件:
     /*
        Check if we're in subquery that is a candidate for flattening into a
        semi-join (which is done in flatten_subqueries()). The requirements are:
          1. Subquery predicate is an IN/=ANY subquery predicate
          2. Subquery is a single SELECT (not a UNION)
          3. Subquery does not have GROUP BY
          4. Subquery does not use aggregate functions or HAVING
          5. Subquery predicate is at the AND-top-level of ON/WHERE clause
            a) select xxx from t1 where t1.xxx in ()
            b) select t1.xx from t1 join t2 on  t2.xx2 in ( )
          6. We are not in a subquery of a single table UPDATE/DELETE that 
               doesn't have a JOIN (TODO: We should handle this at some
               point by switching to multi-table UPDATE/DELETE)
               rm 单表的UPDATE/DELETE的子查询是不行,因为UPDATE/DELETE没有join一说
          7. We're not in a confluent table-less subquery, like "SELECT 1".
              rm  无表子句不处理
          8. No execution method was already chosen (by a prepared statement)
          9. Parent select is not a confluent table-less select
             rm  父表不能是无表查询 如 select 1
          10. Neither parent nor child select have STRAIGHT_JOIN option.
      */
 
 b)物化标识子查询
   将非相关子查询保存到临时表中或者缓存中。
 c) 执行in 向 exists转换(优点:这样exists就可以使用半连接的优化策略了)
   例子:
    outer_expr IN (SELECT inner_expr FROM … WHERE subquery_where) -->
    EXISTS (SELECT 1 FROM … WHERE subquery_where AND outer_expr=inner_expr)
   但是还有两种情况需要考虑outer_expr为null的情况和inner_expr为null的情况,这两点也是我比较疑惑的地方
   例如,有如下表a1:
   http://onexin.iyunv.com/source/plugin/onexin_bigdata/https://askdba.alibaba-inc.com/services/getTfsFile.do?filePath=TB1YrKxKXXXXXbNXXXXXXXXXXXX.jpg
    我执行select * from a1 where id in ( select df from a1)  -->无记录 
    select * from a1 as t1 where exists ( select 1 from a1 t2 where (t1.id = t2.df or t2.df is null ) )  --> 三条记录
    结果竟然不一样!这是为什么呢?
 d)<op> ALL/ANY/SOME 向MIN MAX转换 OP为大于或者小于操作,例子会将子查询转化成下面两种其中的一种。
   例子:(oe 外表的表达式, ie 内表的表达式, cmp为 ">  <") 
    oe $cmp$ (SELECT ie FROM ... WHERE subq_where ... HAVING subq_having) --> 
    oe $cmp$ (SELECT MAX(...) )  // handled by Item_singlerow_subselect
    oe $cmp$ <max>(SELECT ...)   // handled by Item_maxmin_subselect
   前置条件:
      /*
        If this is an ALL/ANY single-value subquery predicate, try to rewrite
        it with a MIN/MAX subquery.
    
        E.g. SELECT * FROM t1 WHERE b > ANY (SELECT a FROM t2) can be rewritten
        with SELECT * FROM t1 WHERE b > (SELECT MIN(a) FROM t2).
    
        A predicate may be transformed to use a MIN/MAX subquery if it:
        1. has a greater than/less than comparison operator, and
        2. is not correlated with the outer query, and
            rm  非相关子查询(与外表无关) select t1.xx from t1 where t1.xxx > any ( select xxx from t2 where t2.xx = 123 )
        3. UNKNOWN results are treated as FALSE, or can never be generated, and
            rm 未知结果被当成false处理,或者永远不会生成未知结果
      */
 e) 使用值替代标量子查询
  (标量子查询:select (subquery) from table)其中subquery只能为单列和单值。
 
 
 

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-287693-1-1.html 上篇帖子: mysql中float、double数据类型的问题 下篇帖子: 转:concat和concat_ws()区别及MySQL的几个实用字符串函数
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表