改善这个慢mysql查询的技巧?

发布于 2024-07-14 01:18:33 字数 850 浏览 7 评论 0原文

我使用的查询通常在一秒内执行,但有时需要 10-40 秒才能完成。 实际上我并不完全清楚子查询是如何工作的,我只知道它是有效的,因为它为每个 faverprofileid 提供了 15 行。

我正在记录缓慢的查询,它告诉我检查了 5823244 行,这很奇怪,因为在涉及的任何表中都没有接近这么多行(收藏夹表最多有 50,000 行)。

有人能给我一些指点吗? 子查询有问题并且需要使用文件排序吗?

编辑:运行解释显示用户表没有使用索引(即使 id 是主键)。 在额外的下面写着:使用临时的; 使用文件排序。

SELECT F.id,F.created,U.username,U.fullname,U.id,I.*   
FROM favorites AS F  
INNER JOIN users AS U ON F.faver_profile_id = U.id  
INNER JOIN items AS I ON F.notice_id = I.id  
WHERE faver_profile_id IN (360,379,95,315,278,1)  
AND F.removed = 0  
AND I.removed = 0   
AND F.collection_id is null   
AND I.nudity = 0  
AND (SELECT COUNT(*) FROM favorites WHERE faver_profile_id = F.faver_profile_id  
AND created > F.created AND removed = 0 AND collection_id is null) < 15 
ORDER BY F.faver_profile_id, F.created DESC;

I'm using a query which generally executes in under a second, but sometimes takes between 10-40 seconds to finish. I'm actually not totally clear on how the subquery works, I just know that it works, in that it gives me 15 rows for each faverprofileid.

I'm logging slow queries and it's telling me 5823244 rows were examined, which is odd because there aren't anywhere close to that many rows in any of the tables involved (the favorites table has the most at 50,000 rows).

Can anyone offer me some pointers? Is it an issue with the subquery and needing to use filesort?

EDIT: Running explain shows that the users table is not using an index (even though id is the primary key). Under extra it says: Using temporary; Using filesort.

SELECT F.id,F.created,U.username,U.fullname,U.id,I.*   
FROM favorites AS F  
INNER JOIN users AS U ON F.faver_profile_id = U.id  
INNER JOIN items AS I ON F.notice_id = I.id  
WHERE faver_profile_id IN (360,379,95,315,278,1)  
AND F.removed = 0  
AND I.removed = 0   
AND F.collection_id is null   
AND I.nudity = 0  
AND (SELECT COUNT(*) FROM favorites WHERE faver_profile_id = F.faver_profile_id  
AND created > F.created AND removed = 0 AND collection_id is null) < 15 
ORDER BY F.faver_profile_id, F.created DESC;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

睫毛溺水了 2024-07-21 01:18:33

检查的行数表示很大,因为许多行已被检查多次。 您出现此问题的原因是查询计划优化不正确,这会导致本应执行索引查找时进行表扫描。 在这种情况下,检查的行数是指数的,即与多个表中的总行数的乘积相当的数量级。

<代码>选择
F.id,F.创建,U.用户名,U.全名,U.id,I.*
FROM 收藏夹 AS F FORCE INDEX (faver_profile_id_key)
INNER JOIN 用户 AS U FORCE INDEX FOR JOIN(主) ON F.faver_profile_id = U.id
INNER JOIN items AS I FORCE INDEX FOR JOIN (PRIMARY) ON F.notice_id = I.id
其中 faver_profile_id IN (360,379,95,315,278,1)
AND F.removed = 0
AND I.removed = 0
并且 F.collection_id 为空
AND I.nudity = 0
AND (从收藏夹中选择 COUNT(*) 个 强制索引 (faver_profile_id_key) WHERE faver_profile_id = F.faver_profile_id
并创建了> F.创建 AND 删除 = 0 AND collection_id 为空) < 15
ORDER BY F.faver_profile_id, F.created DESC;

您还可以更改查询以使用 GROUP BY faver_profile_id/HAVING count >; 15 而不是嵌套的 SELECT COUNT(*) 子查询,如 vartec 建议的那样。 如果您的原始查询和 vartec 的查询都经过适当优化,例如使用提示(您的查询将使用嵌套索引查找,而 vartec 的查询),则它们的性能应该具有可比性将使用基于哈希的策略。)

The number of rows examined represents is large because many rows have been examined more than once. You are getting this because of an incorrectly optimized query plan which results in table scans when index lookups should have been performed. In this case the number of rows examined is exponential, i.e. of an order of magnitude comparable to the product of the total number of rows in more than one table.

  • Make sure that you have run ANALYZE TABLE on your three tables.
  • Read on how to avoid table scans, and identify then create any missing indexes
  • Rerun ANALYZE and re-explain your queries
    • the number of examined rows must drop dramatically
    • if not, post the full explain plan
  • use query hints to force the use of indices (to see the index names for a table, use SHOW INDEX):

SELECT
F.id,F.created,U.username,U.fullname,U.id,I.*

FROM favorites AS F FORCE INDEX (faver_profile_id_key)
INNER JOIN users AS U FORCE INDEX FOR JOIN (PRIMARY) ON F.faver_profile_id = U.id
INNER JOIN items AS I FORCE INDEX FOR JOIN (PRIMARY) ON F.notice_id = I.id
WHERE faver_profile_id IN (360,379,95,315,278,1)
AND F.removed = 0
AND I.removed = 0
AND F.collection_id is null
AND I.nudity = 0
AND (SELECT COUNT(*) FROM favorites FORCE INDEX (faver_profile_id_key) WHERE faver_profile_id = F.faver_profile_id
AND created > F.created AND removed = 0 AND collection_id is null) < 15
ORDER BY F.faver_profile_id, F.created DESC;

You may also change your query to use GROUP BY faver_profile_id/HAVING count > 15 instead of the nested SELECT COUNT(*) subquery, as suggested by vartec. The performance of both your original and vartec's query should be comparable if both are properly optimized e.g. using hints (your query would use nested index lookups, whereas vartec's query would use a hash-based strategy.)

小鸟爱天空丶 2024-07-21 01:18:33

我认为使用 GROUP BYHAVING 应该会更快。
那是你要的吗?

SELECT F.id,F.created,U.username,U.fullname,U.id, I.field1, I.field2, count(*) as CNT
FROM favorites AS F  
INNER JOIN users AS U ON F.faver_profile_id = U.id  
INNER JOIN items AS I ON F.notice_id = I.id  
WHERE faver_profile_id IN (360,379,95,315,278,1)  
AND F.removed = 0  
AND I.removed = 0   
AND F.collection_id is null   
AND I.nudity = 0  
GROUP BY F.id,F.created,U.username,U.fullname,U.id,I.field1, I.field2
HAVING CNT < 15
ORDER BY F.faver_profile_id, F.created DESC;

不知道您需要 items 中的哪些字段,因此我添加了占位符。

I think with GROUP BY and HAVING it should be faster.
Is that what you want?

SELECT F.id,F.created,U.username,U.fullname,U.id, I.field1, I.field2, count(*) as CNT
FROM favorites AS F  
INNER JOIN users AS U ON F.faver_profile_id = U.id  
INNER JOIN items AS I ON F.notice_id = I.id  
WHERE faver_profile_id IN (360,379,95,315,278,1)  
AND F.removed = 0  
AND I.removed = 0   
AND F.collection_id is null   
AND I.nudity = 0  
GROUP BY F.id,F.created,U.username,U.fullname,U.id,I.field1, I.field2
HAVING CNT < 15
ORDER BY F.faver_profile_id, F.created DESC;

Don't know which fields from items you need, so I've put placeholders.

誰認得朕 2024-07-21 01:18:33

我建议你使用Mysql解释查询来看看你的mysql如何服务器处理查询。 我的赌注是你的指数不是最佳的,但解释应该比我的赌注做得更好。

I suggest you use Mysql Explain Query to see how your mysql server handles the query. My bet is your indexes aren't optimal, but explain should do much better than my bet.

王权女流氓 2024-07-21 01:18:33

您可以对每个 id 执行循环并使用 limit 而不是 count(*) 子查询:

foreach $id in [123,456,789]:
    SELECT
     F.id,
     F.created,
     U.username,
     U.fullname,
     U.id,
     I.*
    FROM
     favorites AS F INNER JOIN
     users AS U ON F.faver_profile_id = U.id INNER JOIN
     items AS I ON F.notice_id = I.id
    WHERE
     F.faver_profile_id = {$id} AND
     I.removed = 0 AND
     I.nudity = 0 AND
     F.removed = 0 AND
     F.collection_id is null
    ORDER BY
     F.faver_profile_id,
     F.created DESC
    LIMIT
     15;

You could do a loop on each id and use limit instead of the count(*) subquery:

foreach $id in [123,456,789]:
    SELECT
     F.id,
     F.created,
     U.username,
     U.fullname,
     U.id,
     I.*
    FROM
     favorites AS F INNER JOIN
     users AS U ON F.faver_profile_id = U.id INNER JOIN
     items AS I ON F.notice_id = I.id
    WHERE
     F.faver_profile_id = {$id} AND
     I.removed = 0 AND
     I.nudity = 0 AND
     F.removed = 0 AND
     F.collection_id is null
    ORDER BY
     F.faver_profile_id,
     F.created DESC
    LIMIT
     15;
茶色山野 2024-07-21 01:18:33

我假设该查询的结果旨在显示为分页列表。 在这种情况下,也许您可​​以考虑执行更简单的“未联接查询”,并对每行执行第二次查询以仅读取显示的 15、20 或 30 个元素。 JOIN 不是一个繁重的操作吗? 这将简化查询,并且当连接的表增长时它不会变得更慢。

如果我错了请告诉我。

I'll suppose the result of that query is intented to be shown as a paged list. In that case, perhaps you could consider to do a simpler "unjoined query" and do a second query for each row to read only the 15, 20 or 30 elements shown. Was not a JOIN a heavy operation? This would simplify the query and It wouldn't become slower when the joined tables grow.

Tell me if I'm wrong, please.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文