我有一个关键字表,我会定期刷新远程搜索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;
答案 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 BY
或ORDER BY
对MySQL中的计划没有很好的控制,但您可以尝试(尝试)通过以下方式改进查询:
在(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
。
在(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_id
,status
,source_id
,created
上是否有适当的索引?也许即使是所有四列的复合索引也是最好的,因为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;