MySQL查询优化;选择多个字段与JOIN

时间:2012-08-24 18:36:18

标签: mysql sql database query-optimization

我们有一个相对简单的查询,可以在4个表中执行LEFT JOIN。 A是“主”表或层次结构中的最顶层表。 B链接到A,C链接到B.此外,X链接到A.所以层次结构基本上是

A
C => B => A
X => A

查询基本上是:

SELECT
    a.*, b.*, c.*, x.*
FROM
    a
    LEFT JOIN b ON b.a_id = a.id
    LEFT JOIN c ON c.b_id = b.id
    LEFT JOIN x ON x.a_id = a.id
WHERE
    b.flag = true
ORDER BY
    x.date DESC
LIMIT 25

通过EXPLAIN,我已确认正确的索引已到位,并且内置的MySQL查询优化器正确且正确地使用这些索引

所以这是奇怪的部分......

当我们按原样运行查询时,运行大约需要1.1秒。

然而,经过一些检查后,似乎如果我删除了大部分SELECT字段,我会得到显着速度提升。

因此,如果我们将其分为两步查询过程:

  1. 首先查询与上述相同,只是将SELECT子句更改为SELECT a.id而不是SELECT *
  2. 第二个查询也与上面相同,只是将WHERE子句更改为仅a.id IN再次执行查询1的结果而不是之前的结果
  3. 结果截然不同。第一个查询为.03秒,第二个查询为.02。

    在代码中执行这两步查询实际上为我们提供了20倍的性能提升。

    所以这是我的问题:

    这种类型的优化不应该在数据库引擎中完成吗?为什么实际SELECT的哪些字段的差异会对查询的整体性能产生影响?

    在一天结束时,它只是选择完全相同的25行并返回这25行完全相同的全部内容。那么,为什么性能差异很大?

    ADDED 2012-08-24 13:02 PM PDT

    感谢eggyal和invertedSpear的反馈意见。首先,它不是一个缓存问题 - 我在每个方法之间交替执行多次运行两次查询(大约10次)的测试。第一个(单个查询)方法的结果平均为1.1秒,第二个(2个查询)方法的平均结果为.03 + .02秒。

    就索引而言,我认为我已经做了一个EXPLAIN来确保我们通过密钥,而且大多数情况下我们都是。但是,我刚刚再次进行了快速检查,并注意到一件有趣的事情:

    较慢的“单一查询”方法未显示第三行的“使用索引”的额外注释:

    +----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
    | id | select_type | table | type   | possible_keys          | key               | key_len | ref                           | rows | Extra                                        |
    +----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
    |  1 | SIMPLE      | t1    | index  | PRIMARY                | shop_group_id_idx | 5       | NULL                          |  102 | Using index; Using temporary; Using filesort |
    |  1 | SIMPLE      | t2    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t1.organization_id |    1 | Using where                                  |
    |  1 | SIMPLE      | t0    | ref    | bundle_idx,shop_id_idx | shop_id_idx       | 4       | dbmodl_v18.t1.organization_id |  309 |                                              |
    |  1 | SIMPLE      | t3    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t0.id              |    1 |                                              |
    +----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
    

    显示“使用索引”时,我们只查询ID:

    +----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
    | id | select_type | table | type   | possible_keys          | key               | key_len | ref                           | rows | Extra                                        |
    +----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
    |  1 | SIMPLE      | t1    | index  | PRIMARY                | shop_group_id_idx | 5       | NULL                          |  102 | Using index; Using temporary; Using filesort |
    |  1 | SIMPLE      | t2    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t1.organization_id |    1 | Using where                                  |
    |  1 | SIMPLE      | t0    | ref    | bundle_idx,shop_id_idx | shop_id_idx       | 4       | dbmodl_v18.t1.organization_id |  309 | Using index                                  |
    |  1 | SIMPLE      | t3    | eq_ref | PRIMARY                | PRIMARY           | 4       | dbmodl_v18.t0.id              |    1 |                                              |
    +----+-------------+-------+--------+------------------------+-------------------+---------+-------------------------------+------+----------------------------------------------+
    

    奇怪的是,两者都列出了正在使用的正确索引......但我想它会引发问题:

    为什么它们不同(考虑到所有其他条款完全相同)?这是否表明为什么它变慢?

    不幸的是,MySQL文档没有提供有关EXPLAIN结果中“额外”列为空/空的详细信息。

1 个答案:

答案 0 :(得分:1)

比速度更重要的是,您的查询逻辑存在缺陷。在WHERE子句中测试LEFT JOINed列时(除了测试NULL),您强制该连接的行为就像它是INNER JOIN一样。相反,你想要:

SELECT
    a.*, b.*, c.*, x.*
FROM
    a
    LEFT JOIN b ON b.a_id = a.id
        AND b.flag = true
    LEFT JOIN c ON c.b_id = b.id
    LEFT JOIN x ON x.a_id = a.id
ORDER BY
    x.date DESC
LIMIT 25

我的下一个建议是检查SELECT中的所有.*。你真的需要所有 所有表中的列吗?