如何优化查询以查找不存在条件连接行的行?

时间:2009-02-13 20:30:40

标签: mysql optimization join

我有一个关键字表,我会定期刷新远程搜索API,每次刷新其中一个关键字时,我都会有另一个表。我使用此表来阻止多个进程相互踩踏并刷新相同的关键字以及stat集合。因此,当我启动我的程序时,它会查询当前正在处理请求的所有关键字,并且在最后15分钟内没有成功的关键字,或者无论间隔是什么。一切都运行良好一段时间,但现在keywords_requests表中有近200万行,而且事情正在严重受阻。我在keywords_requests表中的几乎每一列都有索引,但无济于事。

我正在记录慢速查询,而且正如你所看到的那样,这个问题正在进行中。我该怎么办?

# Query_time: 20 Lock_time: 0 Rows_sent: 568 Rows_examined: 1826718

SELECT Keyword.id, Keyword.keyword
FROM `keywords` as Keyword
LEFT JOIN `keywords_requests` as KeywordsRequest
ON (
  KeywordsRequest.keyword_id = Keyword.id
  AND (KeywordsRequest.status = 'success' OR KeywordsRequest.status = 'active')
  AND KeywordsRequest.source_id = '29'
  AND KeywordsRequest.created > FROM_UNIXTIME(1234551323)
)
WHERE KeywordsRequest.id IS NULL
GROUP BY Keyword.id
ORDER BY KeywordsRequest.created ASC;

4 个答案:

答案 0 :(得分:2)

Keywords上最具选择性的索引似乎是KeywordRequest.created上的索引。

尝试以这种方式重写查询:

SELECT Keyword.id, Keyword.keyword
FROM `keywords` as Keyword
LEFT OUTER JOIN (
  SELECT *
  FROM `keywords_requests` as kr
  WHERE created > FROM_UNIXTIME(1234567890) /* Happy unix_time! */
) AS KeywordsRequest
ON (
  KeywordsRequest.keyword_id = Keyword.id
  AND (KeywordsRequest.status = 'success' OR KeywordsRequest.status = 'active')
  AND KeywordsRequest.source_id = '29'
)
WHERE keyword_id IS NULL;

它(希望)散列加入两个不那么大的来源。

Bill Karwin是对的,你不需要GROUP BYORDER BY

对MySQL中的计划没有很好的控制,但您可以尝试(尝试)通过以下方式改进查询:

  1. (keyword_id, status, source_id, created)上创建一个复合索引,并将其设为:

    SELECT Keyword.id, Keyword.keyword
    FROM `keywords` as Keyword
    LEFT OUTER JOIN `keywords_requests` kr
    ON (
      keyword_id = id
      AND status = 'success'
      AND source_id = '29'
      AND created > FROM_UNIXTIME(1234567890)
    )
    WHERE keyword_id IS NULL
    UNION
    SELECT Keyword.id, Keyword.keyword
    FROM `keywords` as Keyword
    LEFT OUTER JOIN `keywords_requests` kr
    ON (
      keyword_id = id
      AND status = 'active'
      AND source_id = '29'
      AND created > FROM_UNIXTIME(1234567890)
    )
    WHERE keyword_id IS NULL
    

    理想情况下,您应该在索引上使用NESTED LOOPS

  2. (status, source_id, created)上创建一个复合索引,并将其设为:

    SELECT Keyword.id, Keyword.keyword
    FROM `keywords` as Keyword
    LEFT OUTER JOIN (
      SELECT *
      FROM `keywords_requests` kr
      WHERE
        status = 'success'
        AND source_id = '29'
        AND created > FROM_UNIXTIME(1234567890)
      UNION ALL
      SELECT *
      FROM `keywords_requests` kr
      WHERE
        status = 'active'
        AND source_id = '29'
        AND created > FROM_UNIXTIME(1234567890)
    )
    ON keyword_id = id
    WHERE keyword_id IS NULL
    

    希望在更受限制的哈希表上使用HASH JOIN

答案 1 :(得分:1)

在诊断MySQL查询性能时,您需要分析的首要事项之一是来自EXPLAIN的报告。

如果您学习阅读EXPLAIN提供给您的信息,那么您可以查看查询无法使用索引的位置,或者它们导致昂贵的文件排序或其他性能危险信号的位置。

我注意到在你的查询中,GROUP BY是无关紧要的,因为KeywordRequests只返回一个NULL行。此外,ORDER BY是无关紧要的,因为由于WHERE子句,您将按列排序,该列始终为NULL。如果删除这些子句,则可能会删除一个文件排序。

还考虑将查询重写为其他形式,并测量每个表单的性能。例如:

SELECT k.id, k.keyword
FROM `keywords` AS k
WHERE NOT EXISTS (
  SELECT * FROM `keywords_requests` AS kr
  WHERE kr.keyword_id = k.id
   AND kr.status IN ('success', 'active')
   AND kr.source_id = '29'
   AND kr.created > FROM_UNIXTIME(1234551323)
);

其他提示:

  • kr.source_id是整数吗?如果是,则与整数29而不是字符串'29'进行比较。
  • keyword_idstatussource_idcreated上是否有适当的索引?也许即使是所有四列的复合索引也是最好的,因为MySQL在给定查询中每个表只使用一个索引。

您输入了EXPLAIN输出的屏幕截图,并在评论中发布了一个链接。我看到查询是使用来自关键字的索引,这是有道理的,因为您无论如何都要扫描该表中的每一行。短语“不存在”表示MySQL已经优化了LEFT OUTER JOIN。

我认为这应该比原始查询有所改进。 GROUP BY / ORDER BY可能导致它将中间数据集保存为临时表,并将其排序到磁盘上(这非常慢!)。您要查找的是EXPLAIN信息的Extra列中的“Using temporary; using filesort”。

所以你现在已经足够改进它以缓解现在的瓶颈。

我注意到可能的键可能表示您在四列上有单独的索引。您可以通过创建复合索引来改进它:

CREATE INDEX kr_cover ON keywords_requests
  (keyword_id, created, source_id, status);

您可以为MySQL hint使用特定索引:

... FROM `keywords_requests` AS kr USE INDEX (kr_cover) WHERE ...

答案 2 :(得分:0)

关于MySQL的Dunno,但在MSSQL中我会采取的攻击线是:

1)在KeywordsRequest status,source_id和created

上创建覆盖索引

2)UNION结果在关于KeywordsRequest.status

的OR周围

3)使用NOT EXISTS而不是Outer Join(并尝试使用UNION代替OR)

答案 3 :(得分:0)

试试这个 SELECT Keyword.id,Keyword.keyword 来自keywords作为关键字 LEFT JOIN(select * from keywords_requests,其中source_id ='29'和(status ='success'或status ='active')   AND source_id = '29'   AND创建> FROM_UNIXTIME(1234551323)   AND id为NULL )作为KeywordsRequest 上 (   KeywordsRequest.keyword_id = Keyword.id

) GROUP BY Keyword.id ORDER BY KeywordsRequest.created ASC;