【问题标题】:Tips for improving this slow mysql query?改善这个缓慢的 mysql 查询的技巧?
【发布时间】:2010-10-10 00:43:24
【问题描述】:

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

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

谁能给我一些建议?是子查询的问题,需要使用文件排序吗?

编辑:运行解释表明 users 表没有使用索引(即使 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;

【问题讨论】:

  • 如果您将“select”、“from”、“where”、“and”、“in”和“order by”关键字用小写字母和“inner”编写,您可以获得更好的语法着色"、"join"、"on" 和 "desc",只有第一个大写字母。
  • 您是否尝试为 created 排序的每个 faver_profile_id 选择前 15 个项目?
  • 您是否在表格上运行过 ANALYZE?
  • 用户表未使用索引,收藏夹使用“已删除”索引而不是 faver_profile_id。出于某种原因,删除“已删除”索引(我并不真正需要)解决了这两个问题。我也切换到 GROUP BY 和 HAVING 并且查询现在运行得非常快。谢谢!

标签: mysql performance subquery cbo


【解决方案1】:

检查的行数表示很大,因为许多行已被多次检查。 由于查询计划优化不当导致在应该执行索引查找时导致表扫描。在这种情况下,检查的行数是指数级的,即与多个表中的总行数的乘积相当的数量级。

  • 确保您在三个表上运行ANALYZE TABLE
  • 阅读how to avoid table scans,识别并创建任何缺失的索引
  • 重新运行 ANALYZE 并重新解释您的查询
    • 检查的行数必须急剧下降
    • 如果没有,发布完整的解释计划
  • 使用query hints 强制使用索引(要查看表的索引名称,请使用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 @ 987654340@ WHERE faver_profile_id = F.faver_profile_id
AND created &gt; F.created AND removed = 0 AND collection_id is null) &lt; 15
ORDER BY F.faver_profile_id, F.created DESC;

您也可以按照vartec 的建议将查询更改为使用GROUP BY faver_profile_id/HAVING count &gt; 15 而不是嵌套的SELECT COUNT(*) 子查询。如果您的原始查询和vartec 查询的性能都经过适当优化,例如使用提示(您的查询将使用嵌套索引查找,而vartec 的查询将使用基于哈希的策略。)

【讨论】:

    【解决方案2】:

    我认为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 中的哪些字段,所以我放置了占位符。

    【讨论】:

    • 当我读到他的问题时,我首先想到的是使用 group by 和 have...
    【解决方案3】:

    我建议您使用Mysql Explain Query 来查看您的mysql 服务器如何处理查询。我敢打赌,你的索引不是最优的,但解释应该比我的赌注做得更好。

    【讨论】:

    • 是的,运行 explain 表明 users 表没有使用索引(即使 id 是主键)。在额外下它说:使用临时;使用文件排序。不知道为什么它不使用索引..
    【解决方案4】:

    您可以对每个 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;
    

    【讨论】:

      【解决方案5】:

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

      如果我错了,请告诉我。

      【讨论】:

        猜你喜欢
        • 2022-01-04
        • 1970-01-01
        • 2022-01-02
        • 2016-04-16
        • 1970-01-01
        • 2014-01-16
        • 1970-01-01
        • 1970-01-01
        • 2011-08-04
        相关资源
        最近更新 更多