【问题标题】:How to optimize MySQL "Order By Limit 1" in queries that join multiple tables?如何在连接多个表的查询中优化 MySQL “Order By Limit 1”?
【发布时间】:2017-01-29 09:43:21
【问题描述】:

所以我有一个这样的查询:

SELECT tablea.name, tablea.views from tablea inner 
join tableb on (tablea.id = tableb.id and tablea.balance > 0) 
order by tablea.views asc limit 1

但是,问题是当我运行它时,它运行得很慢(4 秒以上)。 有趣的是,当删除“order by”子句时,在保持限制 1 的同时,它会在 0.005 秒(大约)内运行。

更有趣的是:当我不将它加入tableb时,即:

SELECT tablea.name, tablea.views from tablea 
where tablea.balance > 0 
order by tablea.views asc limit 1

查询通常在 0.005 秒内运行。

注意事项:

  • tablea 中的列视图已编入索引
  • tablea 和 tableb 在 id 方面具有 1 对 1 的关系,并且具有大致相同的行数。

为什么第一个查询、删除 'order by' 后的第一个查询和第二个查询之间的性能差异如此之大?

在连接两个表时,无论如何可以使排序更快吗?

【问题讨论】:

  • 您可以在原始连接查询上运行EXPLAIN 吗?让我们看看那里实际发生了什么。
  • 关于性能的问题总是需要所有相关表的 CREATE TABLE 语句以及 EXPLAIN 的结果

标签: php mysql indexing sql-order-by sql-limit


【解决方案1】:

关于这里发生的事情的一个可能解释是 MySQL 选择在执行实际连接之前进行排序。正如您在删除 ORDER BY 子句时在原始查询中看到的那样,连接本身并不是性能问题。解决此问题的一种方法是将原始查询包装在子查询中,然后对其进行排序:

SELECT *
FROM
(
    SELECT tablea.name,
           tablea.views
    FROM tablea
    INNER JOIN tableb
        ON tablea.id = tableb.id AND
           tablea.balance > 0
) t
ORDER BY t.views ASC
LIMIT 1

如果这可行,那么它可能证实了我的推测。在这种情况下,子查询强制 MySQL 只对实际子查询产生的记录进行排序。无论如何,您应该养成在此类查询上运行EXPLAIN 的习惯。我的猜测是在加入原始查询时索引没有被使用/有效。

参考:Slow query when using ORDER BY

【讨论】:

    【解决方案2】:
    Given INDEX(x)
    ORDER BY x LIMIT 1
    

    将方便地使用索引并选择第一项

    Given INDEX(x)
    WHERE ...
    ORDER BY x LIMIT 1
    

    也可以使用索引,希望WHERE 满足一些早期行。如果没有,那么它可能必须扫描整个表才能找到一行!

    Given INDEX(a, x)
    WHERE a = 12
    ORDER BY x LIMIT 1
    

    没问题 -- 在索引中查找 a=12;选择第一项。

    Given INDEX(a, x)
    WHERE a > 12
    ORDER BY x LIMIT 1
    

    现在索引不太好。它将需要提取所有 a>12 的行,按 x 排序,然后交付一行。

    一般来说如果WHEREORDER BY可以完全满足,那么LIMIT n可以优化。 (这假定没有GROUP BY,或者GROUP BYORDER BY相同。)

    只有一张桌子。当您JOIN 两个(或更多)表时,它会变得更加混乱。对于两个表,优化器选择一个表,在那里找到它可以找到的内容,然后对另一个表执行嵌套循环连接。

    通常(并非总是),WHERE 子句(在一张桌子上)告诉优化器“选择我”。如果这与ORDER BY 是同一张桌子,那么上面的讨论可能会开始。

    如果没有WHERE 子句,优化器通常从较小的表开始。 (注意:表格大小基于行估计,可能并非每次都正确。)

    使用WHERE EXISTS ( ... tableb ... ) 而不是JOIN tableb... 可能会加快您的第一个查询。优化器会认为这是值得优化的东西。

    等等等等等等。

    请注意,您的“0.005 秒”是“运气”。

    如果您想更深入地挖掘,请提供SHOW CREATE TABLE(以便我们查看索引等)、EXPLAIN SELECT(以便我们讨论优化器的决定),如果可能,请提供EXPLAIN FORMAT=JSON SELECT ...更多细节。另见my indexing cookbook

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多