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

Mysql 如何优化查询以查找不存在条件联接行的行?,mysql,optimization,join,Mysql,Optimization,Join,我有一个关键字表,我会根据远程搜索API定期刷新,还有另一个表,每次刷新一个关键字时都会得到一行。我使用此表阻止多个进程相互踩在一起并刷新同一关键字以及stat集合。因此,当我启动我的程序时,它会查询当前没有请求的所有关键字,并且在过去15分钟内没有成功的请求,或者不管间隔是什么。这一切在一段时间内都很正常,但现在keywords_requests表中有近200万行,情况严重恶化。我在keywords_requests表中的几乎每一列上都有索引,但没有用 我正在记录缓慢的查询,正如您所看到的,这

我有一个关键字表,我会根据远程搜索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;
不知道MySQL,但在MSSQL中,我会采取以下攻击方式:

1在关键字Request status、source_id和created上创建覆盖索引

2将结果合并到关键字request.status上的或上


3使用NOT EXISTS代替外部连接,并尝试使用UNION代替或太多

不知道MySQL,但在MSSQL中,我会采取的攻击路线是:

1在关键字Request status、source_id和created上创建覆盖索引

2将结果合并到关键字request.status上的或上


3使用NOT EXISTS代替外部联接,并尝试使用UNION代替OR TOW

诊断MySQL查询性能时,首先需要分析的是来自的报告

如果您学会了阅读EXPLAIN提供的信息,那么您可以看到查询在哪里没有使用索引,或者在哪里导致了昂贵的文件排序,或者其他性能危险信号

我注意到在您的查询中,groupby是不相关的,因为关键字请求只返回一个空行。此外,orderby是不相关的,因为您是按一列排序的,由于WHERE子句,该列将始终为空。如果删除这些子句,可能会删除文件排序

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

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”进行比较。 是否有关于关键字\u id、状态、源\u id、已创建的适当索引?也许即使是对所有四列的复合索引也是最好的,因为MySQL在给定查询中每个表只使用一个索引。 你做了一个解释输出的屏幕截图,并在评论中发布了一个链接。我发现查询没有使用关键字索引,这很有意义,因为您正在扫描该表中的每一行。短语notexists表示MySQL对左侧外部连接进行了一些优化

我认为这应该比您最初的查询有所改进。GROUP BY/ORDER BY可能导致它将中间数据集保存为临时表,并在磁盘上对其进行排序,这非常慢!。你要找的是使用临时的;在解释信息的额外列中使用文件排序

因此,您可能已经对其进行了足够的改进,以缓解目前的瓶颈

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

CREATE INDEX kr_cover ON keywords_requests
  (keyword_id, created, source_id, status);
您可以为MySQL提供一个特定的索引:

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

在诊断MySQL查询性能时,首先需要分析的是来自的报告

如果您学会了阅读EXPLAIN提供的信息,那么您可以看到查询在哪里没有使用索引,或者在哪里导致了昂贵的文件排序,或者其他性能危险信号

我注意到在您的查询中,groupby是不相关的,因为关键字请求只返回一个空行。此外,orderby是不相关的,因为您是按一列排序的,由于WHERE子句,该列将始终为空。如果删除这些子句,可能会删除文件排序

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

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”进行比较。 是否有关于关键字\u id、状态、源\u id、已创建的适当索引?也许即使是对所有四列的复合索引也是最好的,因为MySQL在给定查询中每个表只使用一个索引。 你做了一个解释输出的屏幕截图,并在评论中发布了一个链接。我发现查询没有使用关键字索引,这很有意义,因为您正在扫描该表中的每一行。短语notexists表示MySQL对左侧外部连接进行了一些优化

我认为这应该比您最初的查询有所改进。这群人可能是我 t将中间数据集保存为临时表,并在磁盘上对其排序,这非常慢!。你要找的是使用临时的;在解释信息的额外列中使用文件排序

因此,您可能已经对其进行了足够的改进,以缓解目前的瓶颈

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

CREATE INDEX kr_cover ON keywords_requests
  (keyword_id, created, source_id, status);
您可以为MySQL提供一个特定的索引:

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

看来你对关键词最有选择性的索引是对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;
希望它能将两个不太大的数据源连接起来

比尔·卡温是对的,你不需要分组或点菜

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

在关键字_id、状态、源_id、已创建的上创建一个复合索引,并将其设置为:

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
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
理想情况下,应该在索引上使用嵌套循环

在状态、源id、已创建上创建一个复合索引,并将其设置为:

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
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
这将有望在更受限制的哈希表上使用哈希连接


看来你对关键词最有选择性的索引是对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;
希望它能将两个不太大的数据源连接起来

比尔·卡温是对的,你不需要分组或点菜

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

在关键字_id、状态、源_id、已创建的上创建一个复合索引,并将其设置为:

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
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
理想情况下,应该在索引上使用嵌套循环

在状态、源id、已创建上创建一个复合索引,并将其设置为:

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
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
这将有望在更受限制的哈希表上使用哈希连接

试试这个 选择Keyword.id、Keyword.Keyword 从关键字作为关键字 左连接从关键字\u请求中选择*,其中源\u id='29'和状态='success'或状态='active' 而source_id='29' 并从_UNIXTIME1234551323创建> 并且id为空 as关键字请求 在…上 关键字request.keyword\u id=keyword.id

按关键字分组。id ORDER BY KeywordsRequest.created ASC

试试这个 选择Keyword.id、Keyword.Keyword 从关键字作为关键字 左连接从关键字\u请求中选择*,其中源\u id='29'和状态='success'或状态='active' 而source_id='29' 并从_UNIXTIME1234551323创建> 并且id为空 as关键字请求 在…上 关键字request.keyword\u id=keyword.id

按关键字分组。id
ORDER BY KeywordsRequest.created ASC

哇,在一些情况下,这将查询时间减少到零,而在普通情况下,则不到一秒钟。很不错的。接下来的一个问题是:我看得越远,查询时间就越长,所以我可以使用多列索引进一步优化吗?哇,这在少数情况下将查询时间缩短到零,在常见情况下缩短到不到一秒钟。很不错的。接下来的一个问题是:我看得越远,查询时间越长,所以我可以用多列索引进一步优化吗?哇,谢谢你的帮助。这个问题的解释对我帮助不大。这是我删除GROUP BY和ORDER BY子句后得到的…我在寻找什么?此外,我通过使用下面的Quassnoi示例获得了最大的速度提升。但如果我想进一步加快速度,多列索引还能帮我吗?我是否只想在索引中包含source\u id、status和keyword\u id,因为created是在子查询中创建的?很难猜测,因为优化器对索引的选择部分取决于数据库中数据值的分布。最好的建议是尝试几种不同的方法,测量解释信息以及运行查询的实际时间。哇,谢谢你的帮助。这个问题的解释对我帮助不大。这是我删除GROUP BY和ORDER BY子句后得到的…我在寻找什么?此外,我通过使用下面的Quassnoi示例获得了最大的速度提升。但如果我想进一步加快速度,多列索引还能帮我吗?我是否只想在索引中包含source\u id、status和keyword\u id,因为created是在子查询中创建的?很难猜测,因为优化器对索引的选择部分取决于数据库中数据值的分布。最好的建议是尝试几种不同的方法,测量解释信息以及运行查询的实际时间。