加快最近的查询
speed up most recent query
我正在尝试获取 3 个成功(成功 =1)条最近的记录,然后查看它们的平均响应时间。
我对结果进行了操作,使平均响应始终为 2 毫秒。
我现在 table 中有 20,000 条记录,但我计划有 1-2 百万条记录。 20,000条记录需要40秒,所以我需要优化这个查询。
这里是fiddle:http://sqlfiddle.com/#!9/dc91eb/1/0
fiddle 也包含我的索引,因此如果需要,我愿意添加更多索引。
SELECT proxy,
Avg(a.responsems) AS avgResponseMs,
COUNT(*) as Count
FROM proxylog a
WHERE
a.success = 1
AND ( (SELECT Count(0)
FROM proxylog b
WHERE ( ( b.success = a.success )
AND ( b.proxy = a.proxy )
AND ( b.datetime >= a.datetime ) )) <= 3 )
GROUP BY proxy
ORDER BY avgResponseMs
这是 EXPLAIN
的结果
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
| 1 | PRIMARY | a | index | NULL | proxy | 61 | NULL | 19110 | Using where; Using temporary; Using filesort |
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
| 2 | DEPENDENT SUBQUERY | b | ref | proxy,datetime | proxy | 52 | wwwim_iroom.a.proxy | 24 | Using where; Using index |
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
在你建议窗口函数之前,我使用的是 MariaDB 10.1.21,它是 ~Mysql 5.6 AFAIK
(success, proxy, datetime, responsems)
上的索引应该有所帮助。 success
、proxy
和 datetime
是两个查询共享的列。 datetime
应该在其他两个之后,因为它用于过滤范围,而其他两个过滤点。 responsems
排在最后,因为这是进行计算的列。这样可以直接从索引中获取所需的值。
并且请编辑问题并将 DDL 和 DML 也包含在问题本身中。 fiddle 可能有一天会出现问题,因此这个问题对未来的读者来说毫无用处。
从你的评论回到我的问题,我想我知道你的问题是什么。
如果您有一个有 900 个请求的代理,您的第一个仍然计数 900(等于或更大)。第二数899,第三数898,依此类推。那就是扼杀你的表现的原因。现在添加数以百万计的记录将扼杀您的查询。
您可能想要做的是将最大日期应用于您正在查询的第一个合理的日期。如果您有代理请求,例如时间是(并且都是成功值)
8:00:00
8:00:18
8:00:57
9:02:12
9:15:27
真的在乎8:00:57和9:02和9:15之间的成功时间吗?如果一台计算机在一个小时内受到 activity 的冲击,而在另一个小时内受到光线的冲击,这真的是对成功时间的公平评估吗?
您可能想要的是有一些(您自行决定的)截止时间,例如 3 分钟以内。如果有人在一段时间内甚至没有通过代理恢复工作怎么办。真的是这样吗?再一次,你的判断力
AND ( a.datetime <= b.datetime and b.datetime < date_add( a.datetime, interval 5 minutes )) )) <= 3 )
而且 <= 3 并没有满足您的期望。同样,你最里面的 COUNT(*) 正在计算所有记录 >= a.datetime,所以直到你在给定的一批代理时间结束时你才会得到这些计数。
那么您是在寻找历史平均时间,还是仅寻找给定代理的最近 3 个时间周期。你请求的和查询的可能是两个完全不同的东西。
您可能需要编辑您的原文 post 以进行澄清。我在这里结束,直到我收到可能提供额外帮助的回音。
我建议您尝试使用 window 函数编写查询:
SELECT pl.proxy, Avg(pl.responsems) AS avgResponseMs, COUNT(*) as Count
FROM (SELECT pl.*,
ROW_NUMBER() OVER (PARTITION BY pl.proxy ORDER BY datetime DESC) as seqnum
FROM proxylog pl
WHERE pl.success = 1
) pl
WHERE seqnum <= 3
GROUP BY proxy
ORDER BY avgResponseMs;
为此,您需要 proxylog(success, proxy, datetime, responsems)
上的索引。
在旧版本中,我会将您的子查询版本替换为:
SELECT pl.proxy, Avg(pl.responsems) AS avgResponseMs, COUNT(*) as Count
FROM (SELECT pl.*,
ROW_NUMBER() OVER (PARTITION BY pl.proxy ORDER BY datetime DESC) as seqnum
FROM proxylog pl
WHERE
) pl
WHERE pl.success = 1 AND
pl.datetime >= ANY (SELECT pl2.datetime
FROM proxylog pl2
WHERE pl2.success = pl.success AND
pl2.proxy = pl.proxy
ORDER BY pl2.datetime DESC
LIMIT 1 OFFSET 2
)
GROUP BY proxy
ORDER BY avgResponseMs;
你要的索引同上
我能够模仿 row_number 并遵循@Gordon Linoff 的回答
SELECT pl.proxy, Avg(pl.responsems) AS avgResponseMs, COUNT(*) as Count
FROM (
SELECT
@row_number:=CASE
WHEN @g = proxy
THEN @row_number + 1
ELSE 1
END AS RN,
@g:=proxy g,
pl.*
FROM proxyLog pl,
(SELECT @g:=0,@row_number:=0) as t
WHERE pl.success = 1
ORDER BY proxy,datetime DESC
) pl
WHERE RN <= 3
GROUP BY proxy
ORDER BY avgResponseMs
我正在尝试获取 3 个成功(成功 =1)条最近的记录,然后查看它们的平均响应时间。
我对结果进行了操作,使平均响应始终为 2 毫秒。
我现在 table 中有 20,000 条记录,但我计划有 1-2 百万条记录。 20,000条记录需要40秒,所以我需要优化这个查询。
这里是fiddle:http://sqlfiddle.com/#!9/dc91eb/1/0
fiddle 也包含我的索引,因此如果需要,我愿意添加更多索引。
SELECT proxy,
Avg(a.responsems) AS avgResponseMs,
COUNT(*) as Count
FROM proxylog a
WHERE
a.success = 1
AND ( (SELECT Count(0)
FROM proxylog b
WHERE ( ( b.success = a.success )
AND ( b.proxy = a.proxy )
AND ( b.datetime >= a.datetime ) )) <= 3 )
GROUP BY proxy
ORDER BY avgResponseMs
这是 EXPLAIN
的结果+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
| 1 | PRIMARY | a | index | NULL | proxy | 61 | NULL | 19110 | Using where; Using temporary; Using filesort |
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
| 2 | DEPENDENT SUBQUERY | b | ref | proxy,datetime | proxy | 52 | wwwim_iroom.a.proxy | 24 | Using where; Using index |
+----+--------------------+-------+-------+----------------+-------+---------+---------------------+-------+----------------------------------------------+
在你建议窗口函数之前,我使用的是 MariaDB 10.1.21,它是 ~Mysql 5.6 AFAIK
(success, proxy, datetime, responsems)
上的索引应该有所帮助。 success
、proxy
和 datetime
是两个查询共享的列。 datetime
应该在其他两个之后,因为它用于过滤范围,而其他两个过滤点。 responsems
排在最后,因为这是进行计算的列。这样可以直接从索引中获取所需的值。
并且请编辑问题并将 DDL 和 DML 也包含在问题本身中。 fiddle 可能有一天会出现问题,因此这个问题对未来的读者来说毫无用处。
从你的评论回到我的问题,我想我知道你的问题是什么。
如果您有一个有 900 个请求的代理,您的第一个仍然计数 900(等于或更大)。第二数899,第三数898,依此类推。那就是扼杀你的表现的原因。现在添加数以百万计的记录将扼杀您的查询。
您可能想要做的是将最大日期应用于您正在查询的第一个合理的日期。如果您有代理请求,例如时间是(并且都是成功值)
8:00:00
8:00:18
8:00:57
9:02:12
9:15:27
真的在乎8:00:57和9:02和9:15之间的成功时间吗?如果一台计算机在一个小时内受到 activity 的冲击,而在另一个小时内受到光线的冲击,这真的是对成功时间的公平评估吗?
您可能想要的是有一些(您自行决定的)截止时间,例如 3 分钟以内。如果有人在一段时间内甚至没有通过代理恢复工作怎么办。真的是这样吗?再一次,你的判断力
AND ( a.datetime <= b.datetime and b.datetime < date_add( a.datetime, interval 5 minutes )) )) <= 3 )
而且 <= 3 并没有满足您的期望。同样,你最里面的 COUNT(*) 正在计算所有记录 >= a.datetime,所以直到你在给定的一批代理时间结束时你才会得到这些计数。
那么您是在寻找历史平均时间,还是仅寻找给定代理的最近 3 个时间周期。你请求的和查询的可能是两个完全不同的东西。
您可能需要编辑您的原文 post 以进行澄清。我在这里结束,直到我收到可能提供额外帮助的回音。
我建议您尝试使用 window 函数编写查询:
SELECT pl.proxy, Avg(pl.responsems) AS avgResponseMs, COUNT(*) as Count
FROM (SELECT pl.*,
ROW_NUMBER() OVER (PARTITION BY pl.proxy ORDER BY datetime DESC) as seqnum
FROM proxylog pl
WHERE pl.success = 1
) pl
WHERE seqnum <= 3
GROUP BY proxy
ORDER BY avgResponseMs;
为此,您需要 proxylog(success, proxy, datetime, responsems)
上的索引。
在旧版本中,我会将您的子查询版本替换为:
SELECT pl.proxy, Avg(pl.responsems) AS avgResponseMs, COUNT(*) as Count
FROM (SELECT pl.*,
ROW_NUMBER() OVER (PARTITION BY pl.proxy ORDER BY datetime DESC) as seqnum
FROM proxylog pl
WHERE
) pl
WHERE pl.success = 1 AND
pl.datetime >= ANY (SELECT pl2.datetime
FROM proxylog pl2
WHERE pl2.success = pl.success AND
pl2.proxy = pl.proxy
ORDER BY pl2.datetime DESC
LIMIT 1 OFFSET 2
)
GROUP BY proxy
ORDER BY avgResponseMs;
你要的索引同上
我能够模仿 row_number 并遵循@Gordon Linoff 的回答
SELECT pl.proxy, Avg(pl.responsems) AS avgResponseMs, COUNT(*) as Count
FROM (
SELECT
@row_number:=CASE
WHEN @g = proxy
THEN @row_number + 1
ELSE 1
END AS RN,
@g:=proxy g,
pl.*
FROM proxyLog pl,
(SELECT @g:=0,@row_number:=0) as t
WHERE pl.success = 1
ORDER BY proxy,datetime DESC
) pl
WHERE RN <= 3
GROUP BY proxy
ORDER BY avgResponseMs