如何在连接多个表的查询中优化 MySQL "Order By Limit 1"?
How to optimize MySQL "Order By Limit 1" in queries that join multiple tables?
所以我有这样的查询:
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 秒(大约)内达到 运行s。
更有趣的是:当我不将它加入 tableb 时,即:
SELECT tablea.name, tablea.views from tablea
where tablea.balance > 0
order by tablea.views asc limit 1
通常在 0.005 秒内查询 运行s。
备注:
- tablea 中的列视图已编入索引
- tablea和tableb在id上是一一对应的关系,行数大致相同
为什么第一个查询、删除 'order by' 时的第一个查询和第二个查询在性能上存在如此巨大的差异?
连接两个表时,有没有办法使排序更快?
关于这里发生的事情的一个可能的解释是 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
的习惯。我的猜测是在加入原始查询时索引不是 used/effective。
参考: Slow query when using ORDER BY
Given INDEX(x)
ORDER BY x LIMIT 1
将方便地使用索引并选择第一项
Given INDEX(x)
WHERE ...
ORDER BY x LIMIT 1
也可以使用索引,希望一些早期的行被WHERE
满足。如果没有,那么它可能必须扫描整个 table 才能找到一行 !
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 排序,然后交付一行。
一般来说,如果、WHERE
、ORDER BY
都可以完全满足,那么LIMIT n
就可以优化了。 (假设没有 GROUP BY
,或者 GROUP BY
和 ORDER BY
相同 。)
这是一个 table。当您 JOIN
两个(或更多)table 时,它会变得更加混乱。对于两个 table,优化器选择一个 table,找到它可以在那里找到的内容,然后对另一个 table.
执行嵌套循环连接
通常(不总是),一个 WHERE
子句(在一个 table 上)告诉优化器 "pick me"。如果 table 与 ORDER BY
相同,那么上述讨论可能会开始。
没有 WHERE
子句,优化器通常从较小的 table 开始。 (注意:table 大小基于行 估计 ,可能每次都不正确。)
使用 WHERE EXISTS ( ... tableb ... )
而不是 JOIN tableb...
可能会加快您的第一个查询。优化器会将其视为值得优化的东西。
等等,等等,等等
请注意,您的“0.005 秒”是 "luck"。
如果您想深入挖掘,请提供 SHOW CREATE TABLE
(以便我们可以看到索引等)、EXPLAIN SELECT
(以便我们可以讨论优化器的决定)以及,如果可能 EXPLAIN FORMAT=JSON SELECT ...
了解更多详情。另见 my indexing cookbook .
所以我有这样的查询:
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 秒(大约)内达到 运行s。
更有趣的是:当我不将它加入 tableb 时,即:
SELECT tablea.name, tablea.views from tablea
where tablea.balance > 0
order by tablea.views asc limit 1
通常在 0.005 秒内查询 运行s。
备注:
- tablea 中的列视图已编入索引
- tablea和tableb在id上是一一对应的关系,行数大致相同
为什么第一个查询、删除 'order by' 时的第一个查询和第二个查询在性能上存在如此巨大的差异?
连接两个表时,有没有办法使排序更快?
关于这里发生的事情的一个可能的解释是 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
的习惯。我的猜测是在加入原始查询时索引不是 used/effective。
参考: Slow query when using ORDER BY
Given INDEX(x)
ORDER BY x LIMIT 1
将方便地使用索引并选择第一项
Given INDEX(x)
WHERE ...
ORDER BY x LIMIT 1
也可以使用索引,希望一些早期的行被WHERE
满足。如果没有,那么它可能必须扫描整个 table 才能找到一行 !
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 排序,然后交付一行。
一般来说,如果、WHERE
、ORDER BY
都可以完全满足,那么LIMIT n
就可以优化了。 (假设没有 GROUP BY
,或者 GROUP BY
和 ORDER BY
相同 。)
这是一个 table。当您 JOIN
两个(或更多)table 时,它会变得更加混乱。对于两个 table,优化器选择一个 table,找到它可以在那里找到的内容,然后对另一个 table.
通常(不总是),一个 WHERE
子句(在一个 table 上)告诉优化器 "pick me"。如果 table 与 ORDER BY
相同,那么上述讨论可能会开始。
没有 WHERE
子句,优化器通常从较小的 table 开始。 (注意:table 大小基于行 估计 ,可能每次都不正确。)
使用 WHERE EXISTS ( ... tableb ... )
而不是 JOIN tableb...
可能会加快您的第一个查询。优化器会将其视为值得优化的东西。
等等,等等,等等
请注意,您的“0.005 秒”是 "luck"。
如果您想深入挖掘,请提供 SHOW CREATE TABLE
(以便我们可以看到索引等)、EXPLAIN SELECT
(以便我们可以讨论优化器的决定)以及,如果可能 EXPLAIN FORMAT=JSON SELECT ...
了解更多详情。另见 my indexing cookbook .