一、预聚合技术概述:什么是预聚合、为什么需要预聚合、预聚合的典型应用场景

大家好,我是老张。做了十几年数据库,我见过太多查询慢到让人崩溃的场景。今天咱们聊聊预聚合——一个能大幅降低查询延迟的利器。

说白了,预聚合就是「提前算好,存起来,查的时候直接拿结果」。你想想看,如果每次查询都要扫描几亿行数据,再实时做 sum、count、avg,那得多慢?预聚合的思路就是:在数据写入的时候,就把这些聚合结果算好,存到另一张表里。查询的时候,直接读这张小表,几毫秒就出结果。

核心思想:用存储空间换查询时间。把计算压力从查询阶段转移到写入阶段。

1.1 为什么需要预聚合?

我刚开始做OLAP系统时,遇到过这么个事。一个电商客户,每天产生几千万条订单数据。运营想看「昨天各品类的销售额排名」,每次查询要跑30秒。老板等不了,运营也等不了。

为什么会这样?因为原始数据太「细」了。每条订单包含用户ID、商品ID、下单时间、支付金额、优惠券信息……几十个字段。但运营只关心「品类+日期+销售额」这三个维度。每次查询都要全表扫描,能不慢吗?

预聚合解决的就是这个矛盾。它把高频查询需要的维度提前组合好,算好结果。查询时直接命中,不用再碰原始数据。

具体来说,预聚合有这几个好处:

  • 查询延迟从秒级降到毫秒级——我见过最夸张的案例,从120秒降到20毫秒
  • 降低系统负载——不用每次查询都跑MapReduce或Spark任务
  • 支持高并发——预聚合表通常很小,可以轻松扛住几百甚至上千的QPS
  • 简化查询逻辑——业务方写SQL时,不用再写复杂的group by和聚合函数

我的经验:预聚合不是银弹。它适合「查询模式固定、维度组合有限」的场景。如果查询维度组合太多,预聚合表会爆炸式增长,反而得不偿失。

1.2 预聚合的典型应用场景

预聚合在三个领域用得最多:OLAP分析、实时数仓、监控系统。我分别说说。

1.2.1 OLAP分析场景

OLAP(联机分析处理)是预聚合的「老家」。像Apache Kylin、Druid、ClickHouse这些系统,核心能力就是预聚合。

举个例子。一个广告平台,每天产生10亿条曝光日志。分析师想看「按广告主、按日期、按地域」的曝光量和点击量。如果每次都扫描10亿条数据,再group by三个维度,光shuffle就要几分钟。

预聚合的做法是:在数据导入时,就按(广告主, 日期, 地域)这三个维度组合,算好曝光量和点击量,存到一张小表里。这张小表可能只有几百万行。查询时直接select,秒出结果。

维度组合 原始数据行数 预聚合后行数 查询延迟
广告主+日期+地域 10亿 500万 200ms
广告主+日期 10亿 50万 50ms
广告主 10亿 1万 10ms

你看,维度越粗,预聚合后的数据量越小,查询越快。

1.2.2 实时数仓场景

实时数仓对延迟的要求更苛刻。数据进来后,几秒内就要能查到聚合结果。这时候预聚合几乎是唯一的选择。

我记得有个金融客户,要做实时风控。每笔交易进来后,需要实时计算「该用户过去5分钟的交易总额」。如果每次查询都去扫过去5分钟的交易明细,根本来不及。

解决方案是:用Flink或Spark Streaming做流式预聚合。每来一条交易,就更新用户维度的聚合结果。查询时直接读这个实时更新的聚合表,毫秒级返回。

注意:实时预聚合要处理好「迟到数据」的问题。比如交易已经聚合完了,突然来了一条5分钟前的数据。这时候需要做数据修正,否则聚合结果会不准。我踩过这个坑,后面会详细讲。

1.2.3 监控系统场景

监控系统是预聚合的另一个「重灾区」。像Prometheus、InfluxDB、OpenTSDB这些时序数据库,底层都大量使用预聚合。

监控数据的典型特点是:写入量大、查询模式固定。比如每台机器的CPU使用率,每5秒采集一次。一个月下来,一台机器就有50万条数据。如果集群有1000台机器,那就是5亿条。

但监控查询通常只看最近1小时、最近1天、最近1周的趋势。预聚合的做法是:按时间粒度做降采样。比如:

  • 原始数据:5秒粒度,保留7天
  • 预聚合1:1分钟粒度(取平均值),保留30天
  • 预聚合2:1小时粒度(取平均值),保留1年

查询「最近1周的CPU趋势」时,直接读1小时粒度的预聚合表,数据量只有原始数据的1/720。查询速度自然快得多。

1.3 预聚合的核心逻辑

说了这么多,我画张图帮你理清思路。这张图展示了预聚合在数据链路中的位置和作用。

预聚合技术核心逻辑 数据源 日志/交易/监控 预聚合引擎 实时/离线计算 预聚合表 小表/快速查询 原始数据存储 HDFS/对象存储 查询请求 用户/应用 查询结果 毫秒级返回 💡 核心流程: 数据源 → 预聚合引擎(实时/离线计算)→ 预聚合表(小表)→ 查询直接命中预聚合表,毫秒级返回 原始数据保留在存储层,用于明细查询和数据回溯

这张图展示了预聚合的完整链路。数据源进来后,分两条路走:一条进预聚合引擎,算出聚合结果存到小表;另一条进原始存储,留着查明细用。查询请求直接打预聚合表,毫秒级返回。原始数据只在需要查明细时才用。

1.4 什么时候该用预聚合?

我总结了几条判断标准,你可以对照着看:

  • 查询模式固定——业务方经常查同样的维度组合,比如按天、按品类、按地域
  • 数据量大——单表数据量超过千万级,全表扫描太慢
  • 对延迟敏感——查询需要在秒级甚至毫秒级返回
  • 维度组合可控——预聚合的维度组合不超过几十种,否则存储会爆炸

如果满足以上条件,预聚合就是你的最佳选择。如果不满足,比如查询维度组合有几千种,那预聚合就不太合适。这时候可以考虑其他方案,比如列式存储、物化视图、或者干脆上分布式MPP数据库。

我的建议:刚开始做预聚合时,不要贪多。先选2-3个最常用的维度组合做预聚合,看看效果。效果好再加。我见过太多人一上来就搞几十个预聚合表,结果存储爆了,维护成本也高,最后不得不回退。