如何优化这个 SQL 大型 table 查询的响应时间?
How to optimize the response time of this SQL query of a large table?
我有一个中大型 table(大约 350000 个条目并且还在增加)。我需要从 table 中获取 dev_id 和 var_id 对的每个最后条目。实际上我可以获取它们,但是查询大约需要 20 秒,而且它对我来说是 unacceptable。
我正在使用 MariaDB 在 MySQL 服务器上尝试下一个查询:
select d.dev_id, d.var_id, d.ts, d.value from data_table d
where d.ts > NOW() - INTERVAL 2 DAY
and ts = (SELECT MAX(ts) FROM data_table
WHERE dev_id = d.dev_id
AND var_id = d.var_id)
ORDER BY d.dev_id
table 的结构如下所示:
id | dev_id | frame_number | var_id | value | ts
1 | 2 | 1 | 2 | 65.5 | 2019-10-10 19:56:05
2 | 3 | 5 | 4 | 23 | 2019-10-10 20:56:06
3 | 2 | 1 | 2 | 65.5 | 2019-10-10 20:59:30
. | . | . | . | . | .
. | . | . | . | . | .
. | . | . | . | . | .
300k| 5 | 100 | 7 | -15.23| 2020-10-10 20:59:30
我需要为类似查询获得更快的响应,但我的经验不足以检测查询中的瓶颈
编辑 1:我不能省略 ORDER BY,但省略的改进很低(20 秒对 18.5 秒)
编辑 2:data_table 模式
EDIT 3 及其修复方法:添加 (dev_id、var_id 和 ts) 作为索引(基于多列的索引)。查询现在只需要 0.6 秒
对于此查询:
select d.dev_id, d.var_id, d.ts, d.value
from data_table d
where d.ts > NOW() - INTERVAL 2 DAY and
ts = (SELECT MAX(d2.ts)
FROM data_table d2
WHERE d2.dev_id = d.dev_id AND d2.var_id = d.var_id
)
ORDER BY d.dev_id;
我会推荐两个索引:
data_table(ts, dev_id, var_id, value)
data_table(dev_id, var_id, ts)
第一个是外部查询的覆盖索引。第二个是内部查询的覆盖索引。
假设您有很多重复项,这可能会有所帮助。如果不是,则外部 order by
可能是性能问题的原因。那会有点难以修复。
我倾向于将这些情况归咎于相关子查询,尽管已经建议的索引在这两种情况下都会有所帮助:
SELECT d.dev_id, d.var_id, d.ts, d.value
FROM (
SELECT dev_id, var_id, MAX(ts) AS ts
FROM data_table
WHERE ts > NOW() - INTERVAL 2 DAY
GROUP BY dev_id, var_id
) AS lastTS
INNER JOIN data_table AS d
ON lastTS.dev_id = d.dev_id AND lastTS.var_id = d.var_id AND lastTS.ts = d.ts
-- or, alternatively, USING (dev_id, var_id, ts)
ORDER BY d.dev_id
;
您当前的相关子查询正在为过去两天的每个条目单独执行 ("under the hood")(dev_id, var_id)
;对于具有相同 (dev_id、var_id) 的多个最近条目甚至可能重复。我建议的版本计算最近 2 天内发生的每个 (var_id, dev_id)
的最大值一次,然后将它们加入 table 以查找完整记录。
如果外部查询的中间结果很少,则针对大量数据,更集中的相关查询可能比非相关查询更快;但是如果有大量的中间结果,and/or 相关子查询不会显着降低非相关版本的成本,我发现非相关版本效果更好。
我有一个中大型 table(大约 350000 个条目并且还在增加)。我需要从 table 中获取 dev_id 和 var_id 对的每个最后条目。实际上我可以获取它们,但是查询大约需要 20 秒,而且它对我来说是 unacceptable。
我正在使用 MariaDB 在 MySQL 服务器上尝试下一个查询:
select d.dev_id, d.var_id, d.ts, d.value from data_table d
where d.ts > NOW() - INTERVAL 2 DAY
and ts = (SELECT MAX(ts) FROM data_table
WHERE dev_id = d.dev_id
AND var_id = d.var_id)
ORDER BY d.dev_id
table 的结构如下所示:
id | dev_id | frame_number | var_id | value | ts
1 | 2 | 1 | 2 | 65.5 | 2019-10-10 19:56:05
2 | 3 | 5 | 4 | 23 | 2019-10-10 20:56:06
3 | 2 | 1 | 2 | 65.5 | 2019-10-10 20:59:30
. | . | . | . | . | .
. | . | . | . | . | .
. | . | . | . | . | .
300k| 5 | 100 | 7 | -15.23| 2020-10-10 20:59:30
我需要为类似查询获得更快的响应,但我的经验不足以检测查询中的瓶颈
编辑 1:我不能省略 ORDER BY,但省略的改进很低(20 秒对 18.5 秒)
编辑 2:data_table 模式
EDIT 3 及其修复方法:添加 (dev_id、var_id 和 ts) 作为索引(基于多列的索引)。查询现在只需要 0.6 秒
对于此查询:
select d.dev_id, d.var_id, d.ts, d.value
from data_table d
where d.ts > NOW() - INTERVAL 2 DAY and
ts = (SELECT MAX(d2.ts)
FROM data_table d2
WHERE d2.dev_id = d.dev_id AND d2.var_id = d.var_id
)
ORDER BY d.dev_id;
我会推荐两个索引:
data_table(ts, dev_id, var_id, value)
data_table(dev_id, var_id, ts)
第一个是外部查询的覆盖索引。第二个是内部查询的覆盖索引。
假设您有很多重复项,这可能会有所帮助。如果不是,则外部 order by
可能是性能问题的原因。那会有点难以修复。
我倾向于将这些情况归咎于相关子查询,尽管已经建议的索引在这两种情况下都会有所帮助:
SELECT d.dev_id, d.var_id, d.ts, d.value
FROM (
SELECT dev_id, var_id, MAX(ts) AS ts
FROM data_table
WHERE ts > NOW() - INTERVAL 2 DAY
GROUP BY dev_id, var_id
) AS lastTS
INNER JOIN data_table AS d
ON lastTS.dev_id = d.dev_id AND lastTS.var_id = d.var_id AND lastTS.ts = d.ts
-- or, alternatively, USING (dev_id, var_id, ts)
ORDER BY d.dev_id
;
您当前的相关子查询正在为过去两天的每个条目单独执行 ("under the hood")(dev_id, var_id)
;对于具有相同 (dev_id、var_id) 的多个最近条目甚至可能重复。我建议的版本计算最近 2 天内发生的每个 (var_id, dev_id)
的最大值一次,然后将它们加入 table 以查找完整记录。
如果外部查询的中间结果很少,则针对大量数据,更集中的相关查询可能比非相关查询更快;但是如果有大量的中间结果,and/or 相关子查询不会显着降低非相关版本的成本,我发现非相关版本效果更好。