You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何加速700万条记录的SQL聚合查询?预聚合表是否有效?

客户销售汇总报表性能优化方案解析

先直接给你明确结论:创建你计划的预聚合表绝对能大幅提速报表查询,尤其是用户选择全年这类大范围日期的场景,效果会非常明显。接下来我详细拆解原因,再结合你提到的数据库现状给你其他优化建议:

一、预聚合表的提速逻辑

你打算做的预聚合表(按customer_id+媒体+产品类别+产品部门分组存储求和结果),核心是把原本每次查询都要做的「全表扫描+实时聚合计算」提前完成并持久化:

  • 原查询要从700万条原始数据里筛选、聚合,全年查询耗时60秒;预聚合后只需要从250万条已经算好的聚合数据里筛选,不仅读取的数据量少了近三分之二,还省去了实时分组求和的CPU消耗,IO和CPU开销都能大幅降低。
  • 对于用户自定义维度的查询(比如只看客户+媒体的销售数据),预聚合表也能快速适配——因为它是最细维度的聚合,上层维度的统计可以直接基于预聚合表再次快速计算,比从原始表重新跑快太多。

二、结合你场景的其他优化建议

你提到「分表加索引的库反而查询更慢」,这种情况大概率是分表策略不合理(比如分表键选得不对,导致跨表查询要合并大量分片数据),或者索引设计冗余。针对你的情况,还有这些优化方向:

1. 给原单表加精准的联合索引(别盲目分表)

你的原单表全表扫描速度还能接受,那不如先给原表加合适的索引试试:

  • 针对核心查询:select customer_id, sum(value) from data where date between ? and ? group by customer_id,可以创建覆盖联合索引(date, customer_id, value)。这个索引包含了查询需要的所有字段,查询时不需要回表到原始数据,直接从索引里就能完成过滤、分组、求和,能大幅加速原始查询。
  • 如果要支持自定义维度(比如加上媒体/产品类别),可以考虑创建复合前缀索引,比如(date, media, customer_id, value),但要注意:索引越多,写入数据时的维护成本越高,需要根据实际查询频率权衡。

2. 预聚合表要做好增量更新

预聚合表不能只初始化一次,得保证和原始表数据同步:

  • 可以用定时任务(比如每天凌晨)同步前一天的原始数据,更新预聚合表对应的聚合值;
  • 如果需要准实时报表,可以用数据库触发器(但要注意触发器会增加写入延迟),或者基于binlog的CDC工具来实时更新预聚合表。

3. 试试数据库的物化视图(如果支持的话)

如果你的数据库支持物化视图(比如PostgreSQL、Oracle,或者MySQL的某些扩展),可以用它替代手动创建的预聚合表——物化视图能自动维护数据同步,省去你自己写同步逻辑的麻烦,本质和预聚合表思路一致,但更省心。

4. 给原表按日期做分区优化

如果原表数据量还在增长,可以考虑给date字段做分区(比如按月分区)。这样查询某个日期范围时,数据库只会扫描对应的分区,而不是全表,能减少大量IO开销。尤其是全年查询,分区后只需要扫描12个分区,比全表扫描快很多。

  • 划重点:分区是逻辑拆分,比物理分表更容易维护,也不会出现跨分片查询的性能问题,适合你这种原单表性能尚可的场景。

5. 优化查询语句的细节

你的查询语句可以做小调整,提升一点性能:

  • ((date >= '2020-05-11' and date <= '2020-06-09'))改成date between '2020-05-11' and '2020-06-09',写法更简洁,数据库执行计划不会有差别,但可读性更好;
  • 确保value字段是数值类型,避免sum(value)出现隐式类型转换,带来额外性能开销。

内容的提问来源于stack exchange,提问作者Harris Vrachimis

火山引擎 最新活动