改进这个慢速mysql查询的提示?

时间:2009-02-26 08:10:01

标签: mysql performance subquery cbo

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

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

有人可以给我一些指示吗?这是子查询的问题,需要使用filesort吗?

编辑:运行说明显示users表没有使用索引(即使id是主键)。额外的说它:使用临时;使用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;

5 个答案:

答案 0 :(得分:6)

检查的行数表示很大,因为已经多次检查了许多行。 由于错误优化的查询计划,您会收到此信息,这会在执行索引查找时导致表扫描。在这种情况下,检查的行数是指数的,即与一个以上表中的总行数的乘积相当的数量级。

  • 确保您在三张桌子上运行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 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;

根据GROUP BY faver_profile_id的建议,您也可以将查询更改为使用HAVING count > 15 / SELECT COUNT(*)而不是嵌套的vartec子查询。如果两者都经过适当优化,那么原始和vartec查询的效果应具有可比性,例如:使用提示(您的查询将使用嵌套索引查找,而vartec的查询将使用基于哈希的策略。)

答案 1 :(得分:5)

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

答案 2 :(得分:3)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

我认为该查询的结果有意显示为分页列表。在这种情况下,也许你可以考虑做一个更简单的“未加入的查询”,并为每一行做第二次查询,只读取显示的15,20或30个元素。是不是一个繁重的操作?这将简化查询,并且当连接表增长时不会变慢。

请告诉我,如果我错了,