如何优化这个大表SQL查询的响应时间?

Posted

技术标签:

【中文标题】如何优化这个大表SQL查询的响应时间?【英文标题】:How to optimize the response time of this SQL query of a large table? 【发布时间】:2020-02-08 05:42:44 【问题描述】:

我有一个中型表(大约 350000 个条目并且还在增长)。我需要从表中获取 dev_id 和 var_id 对的最后一个条目。我实际上可以获取它们,但是查询大约需要 20 秒,而且这对于我的目的来说是不可接受的。

我正在使用 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

表格的结构如下:

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 秒

【问题讨论】:

可以添加索引吗?您需要订购吗? 我可以添加索引(id),我也可以省略 ORDER BY @manespgav 你的 ID 应该已经被索引了,它是主键。您能否向我们展示您的完整架构,包括索引? describe data_table 将是一个好的开始。 explain <your query> 也很有帮助。 @Schwern 附为图片 查看添加的标签。 【参考方案1】:

对于这个查询:

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 可能是性能问题的原因。这会更难解决。

【讨论】:

第一个索引是否需要包含var_idvalue @Schwern 值不是真正的索引,只是数据值【参考方案2】:

我倾向于在这些情况下归咎于相关子查询,尽管已经建议的索引在这两种情况下都会有所帮助:

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
;

您当前的相关子查询正在为过去两天的每个条目单独执行(“在后台”)(dev_id, var_id);对于具有相同(dev_id,var_id)的多个最近条目,甚至可能重复。我建议的版本会计算最近 2​​ 天内发生的每个 (var_id, dev_id) 的最大值一次,然后将它们加入表中以查找完整记录。

如果外部查询的中间结果很少,在处理大量数据时,更集中的相关查询可能比非相关查询更快;但如果有大量中间结果,和/或相关子查询不会显着降低非相关版本的成本,我发现非相关版本效果更好。

【讨论】:

以上是关于如何优化这个大表SQL查询的响应时间?的主要内容,如果未能解决你的问题,请参考以下文章

建议“优化 SQL 查询的响应时间”

减少查询响应时间,需要优化查询

MySQL 对于千万级的大表要怎么优化

PL/SQL Developer 查询中程序停止响应

如何优化限制查询以更快地从大表中访问数据?

如何使大表响应