zoukankan      html  css  js  c++  java
  • MySQL STRAIGHT_JOIN

    问题

    最近在调试一条查询耗时5s多的sql语句,这条sql语句用到了多表关联(inner join),按时间字段排序(order by),时间字段上已经创建了索引(索引名IDX_published_at)。通过explain分析发现,时间字段上的索引没用上(Using temporary和Using filesort),问题很明显,但是原因是什么呢?

    SELECT * FROM news n0_ inner join news_translations n1_ ON n0_.id = n1_.translatable_id inner join channels_news c3_ ON n0_.id = c3_.news_id 
    WHERE 
    ((n0_.unpublished_at IS NOT NULL AND (CURRENT_TIMESTAMP >= n0_.published_at AND CURRENT_TIMESTAMP < n0_.unpublished_at)) OR (CURRENT_TIMESTAMP >= n0_.published_at AND n0_.unpublished_at IS NULL))
    AND (n0_.status = 1 AND n0_.content_type_id = 1) AND n0_.id NOT IN (510466, 510433, 24, 11, 10, 9, 4) 
    AND n0_.home_position_id IS NULL 
    AND 
    n1_.locale = 'zh_CN' 
    AND 
    c3_.channel_id = 1 
    ORDER BY n0_.published_at DESC 
    LIMIT 5 ;

    优化前sql语句

    +-------+--------+-------------------------------+--------+-----------------------------------------------------------+
    | table | type   | key                           | rows   | Extra                                                     |
    +-------+--------+-------------------------------+--------+-----------------------------------------------------------+
    | c3_   | ref    | IDX_87B9249E72F5A1AA          | 161590 | Using where; Using index; Using temporary; Using filesort |
    | n0_   | eq_ref | PRIMARY                       |      1 | Using where                                               |
    | n1_   | ref    | UNIQ_20FDB3302C2AC5D34180C698 |      1 | Using where                                               |
    +-------+--------+-------------------------------+--------+-----------------------------------------------------------+

    explain分析结果 有所删减

    经过一轮折腾的优化,得到了下面的sql语句

    SELECT * FROM news n0_ STRAIGHT_JOIN news_translations n1_ ON n0_.id = n1_.translatable_id STRAIGHT_JOIN channels_news c3_ ON n0_.id = c3_.news_id 
    WHERE 
    ((n0_.unpublished_at IS NOT NULL AND (CURRENT_TIMESTAMP >= n0_.published_at AND CURRENT_TIMESTAMP < n0_.unpublished_at)) OR (CURRENT_TIMESTAMP >= n0_.published_at AND n0_.unpublished_at IS NULL))
    AND (n0_.status = 1 AND n0_.content_type_id = 1) AND n0_.id NOT IN (510466, 510433, 24, 11, 10, 9, 4) 
    AND n0_.home_position_id IS NULL 
    AND 
    n1_.locale = 'zh_CN' 
    AND 
    c3_.channel_id = 1 
    ORDER BY n0_.published_at DESC 
    LIMIT 5 ;

    优化后sql语句

    +-------+--------+-------------------------------+--------+--------------------------+
    | table | type   | key                           | rows   | Extra                    |
    +-------+--------+-------------------------------+--------+--------------------------+
    | n0_   | range  | IDX_published_at              | 255440 | Using where              |
    | n1_   | ref    | UNIQ_20FDB3302C2AC5D34180C698 |      1 | Using where              |
    | c3_   | eq_ref | PRIMARY                       |      1 | Using where; Using index |
    +-------+--------+-------------------------------+--------+--------------------------+

    优化后explain分析结果 有所删减

    优化前后的变化有四点:1、不再Using temporary和Using filesort;2、表的查询顺寻变了;3、查询扫描的rows增加了;4、查询时间由5s降到了0.02s。

    原因分析

    优化前后出现的四点变化,性能显著提升,需要从mysql的关联的连接处理说起。

    以下参考《高性能MySQL》

    1)优化前的sql语句以channels_news为第一个关联表,找到161590条记录;2)优化后的sql语句以news表为第一关联表,找到255440条记录,比第一条sql语句查找多了9W多条。因此,优化前的sql语句的关联顺序是MySQL优化器的选择,可以让查询进行更小的嵌套循环和回溯操作。MySQL通过选择合适的关联顺序来让查询执行的成本尽可能低,重新定义关联的顺序是优化器很重要的一部分功能。不过有时候,优化器给出的并不是最优的关联顺序。这时可以使用STRAIGHT_JOIN关键字重写查询,让优化器按照你认为的最优关联顺序执行。

    从优化后的explain分析结果看出,news是驱动表,结果以news表的published_at字段进行排序,所以用上了索引,避免了Using temporary和Using filesort,自然而然的,查询时间也降下来了。正如前面说的,mysql的优化器通过粗暴的小表驱动大表来选择连接的顺序,第一条sql语句扫描了161590行,第二条sql语句扫描了255440行,优化后的sql语句扫描的行数增加了。

    结语

    结案陈词:造成这次sql语句查询耗时5s的原因是,sql语句order by的字段不在mysql的优化器选在驱动表上,所以导致这次关联查询排序字段上的索引没有被使用。因此,通过使用STRAIGHT_JOIN来强制制定关联查询的表顺序,以达到优化的目的。但是,有时候我们人为地指定顺序不一定比mysql的优化引擎准确,所以在使用STRAIGHT_JOIN的时候三思而后行。

    本文链接:http://www.hcoding.com/?p=211

    原创文章,转载请注明:JC&hcoding.com

    书愤

    陆游

    早岁那知世事艰,中原北望气如山。

    楼船夜雪瓜洲渡,铁马秋风大散关。

    塞上长城空自许,镜中衰鬓已先斑。

    出师一表真名世,千载谁堪伯仲间。

     

  • 相关阅读:
    网络流二十四题之魔术球问题
    网络流二十四题之P2764 最小路径覆盖问题
    网络二十四题 之 P2756 飞行员配对方案问题
    网络流 之 dinic算法
    网络流 之 增广路
    中南
    2249: Altruistic Amphibians 01背包的应用 + lh的简单图论 图转树求lca
    今日训练 搜索
    AD-logon workstation
    Centos7-docker安装
  • 原文地址:https://www.cnblogs.com/szuyuan/p/4273412.html
Copyright © 2011-2022 走看看