【问题标题】:Performance penalty for nested mysql queries嵌套 mysql 查询的性能损失
【发布时间】:2016-11-10 10:53:40
【问题描述】:

SELECT * FROM Table VS SELECT * FROM (SELECT * FROM Table AS A) AS B 的性能损失是多少

我的问题是:首先,SELECT * 是否涉及对表中的行进行迭代,或者它是否会简单地将所有行作为一个块返回而不进行任何迭代(因为没有给出 WHERE 子句),如果是这样,嵌套的示例二中的查询涉及对表进行两次迭代,并且将花费第一次查询的 2 倍时间?谢谢...

【问题讨论】:

    标签: mysql nested


    【解决方案1】:

    这个问题的答案取决于你是使用 5.7 之前的 mysql,还是 5.7 及之后的 mysql。我可能会稍微改变您的问题,但希望以下内容能抓住您的需求。

    您的SELECT * FROM Table 通过聚集索引(物理排序)进行表扫描。在没有主键的情况下,一个是implicitly 可供引擎使用。如您所说,没有where子句。不会尝试过滤或选择其他索引。

    Explain 输出(请参阅also)在其摘要中显示 1 行。这是相对简单的。派生表 B 的解释输出和性能会有所不同,具体取决于您使用的是 5.7 之前的版本还是 5.7 及之后的版本。

    文档Derived Tables in MySQL 5.7 对版本 5.6 和 5.7 进行了很好的描述,后者不会因为物化派生表输出的变化被合并到外部查询中而提供任何惩罚。在以前的版本中,使用派生的临时表会承受大量开销。

    在 5.7 之前测试性能损失非常容易。只需要一张中等大小的表格,就可以看到您的问题的派生表格对性能的影响有多大。下面的例子是在 5.6 版本的一个小桌子上:

    explain 
    select qm1.title  
    from questions_mysql qm1 
    join questions_mysql qm2 
    on qm2.qid<qm1.qid 
    where qm1.qid>3333 and qm1.status='O';
    +----+-------------+-------+-------+-----------------+---------+---------+------+-------+------------------------------------------------+
    | id | select_type | table | type  | possible_keys   | key     | key_len | ref  | rows  | Extra                                          |
    +----+-------------+-------+-------+-----------------+---------+---------+------+-------+------------------------------------------------+
    |  1 | SIMPLE      | qm1   | range | PRIMARY,cactus1 | PRIMARY | 4       | NULL |  5441 | Using where                                    |
    |  1 | SIMPLE      | qm2   | ALL   | PRIMARY,cactus1 | NULL    | NULL    | NULL | 10882 | Range checked for each record (index map: 0x3) |
    +----+-------------+-------+-------+-----------------+---------+---------+------+-------+------------------------------------------------+
    
    
    explain 
    select b.title from 
    (   select qid,title from questions_mysql where qid>3333 and status='O' 
    ) b 
    join questions_mysql qm2 
    on qm2.qid<b.qid; 
    +----+-------------+-----------------+-------+-----------------+---------+---------+------+-------+----------------------------------------------------+
    | id | select_type | table           | type  | possible_keys   | key     | key_len | ref  | rows  | Extra                                              |
    +----+-------------+-----------------+-------+-----------------+---------+---------+------+-------+----------------------------------------------------+
    |  1 | PRIMARY     | qm2             | index | PRIMARY,cactus1 | cactus1 | 10      | NULL | 10882 | Using index                                        |
    |  1 | PRIMARY     | <derived2>      | ALL   | NULL            | NULL    | NULL    | NULL |  5441 | Using where; Using join buffer (Block Nested Loop) |
    |  2 | DERIVED     | questions_mysql | range | PRIMARY,cactus1 | PRIMARY | 4       | NULL |  5441 | Using where                                        |
    +----+-------------+-----------------+-------+-----------------+---------+---------+------+-------+----------------------------------------------------+
    

    请注意,我确实更改了问题,但它说明了派生表及其在优化器中缺少索引使用对 5.7 之前的版本的影响。派生表在实现时受益于索引。但此后它作为临时表承受开销,并在不使用索引的情况下合并到外部查询中。在 5.7 版中不是这种情况

    【讨论】:

    • 很好的解释:)
    • 关于性能问题,在两个子查询之间的连接操作中,是否更喜欢在内部查询而不是外部查询中指定 LIMIT 子句?比如:((SELECT id FROM Table1 LIMIT 10) AS A NATURAL JOIN Table2)) 会比((SELECT id FROM Table1) AS A NATURAL JOIN Table2)) LIMIT 10 好(这可能包含一些与表命名有关的语法错误,但是......)?谢谢:)
    • 最好在您的服务器版本和架构上使用它。以下是在5.6 上,在非服务器级笔记本电脑上。请注意,我从不使用自然连接。我尽量不做出宽泛的笔触答案。尤其是在Explain 输出上可能看起来相同。这种情况在堆栈上发生的太多了。因此,将我们的查询指向更大的数据集,并对我们表现最差的查询或一般调查(如here)执行至少 15 分钟的测试。性能会因优化器而异。
    猜你喜欢
    • 1970-01-01
    • 2016-01-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多