3、预聚合 vs 传统查询:全表扫描的代价、预计算 vs 实时计算、空间换时间的核心思想

这一章,我们来聊聊最核心的对比。

很多人觉得预聚合就是个「缓存」,或者就是个「物化视图」。其实没那么简单。我做了这么多年数据库优化,见过太多团队一上来就搞预聚合,结果存储翻了三倍,查询却没快多少。为什么?因为他们没搞明白预聚合到底在解决什么问题。

说白了,预聚合的核心就一句话:用存储换时间,用预计算换实时计算。

3.1 全表扫描的代价:你根本扛不住

先看一个真实场景。我几年前帮一家电商公司做 BI 系统优化。他们的订单表大概 2 亿行,每天增长 500 万行。业务方要查「过去 30 天每个品类的销售额趋势」。

你猜怎么着?每次查询都要扫 2 亿行。哪怕你建了索引,因为要按品类分组、按日期排序,索引基本废掉。全表扫描,一次 30 秒。业务方一天要跑几百次这种查询。服务器 CPU 直接拉满,磁盘 IO 打满,连带着其他业务都受影响。

全表扫描的代价清单:

  • CPU 飙升:扫描 2 亿行,哪怕只是读数据,CPU 也扛不住
  • 磁盘 IO 打满:机械盘 100MB/s,2 亿行按 100 字节算就是 20GB,光读就要 200 秒
  • 内存不够用:2 亿行数据,Buffer Pool 根本装不下,只能反复读磁盘
  • 并发能力差:一个查询占满资源,其他查询就得排队

为什么会这样?因为传统 OLTP 数据库的设计初衷是「点查询」——查一条记录,走索引,快得很。但分析型查询是「范围查询 + 聚合」,索引基本帮不上忙。

我的经验:当一张表超过 1000 万行,并且你要频繁做 GROUP BY 聚合查询时,全表扫描的代价就已经不可接受了。别等到 2 亿行才想办法。

3.2 预计算 vs 实时计算:两种哲学

好,既然全表扫描不行,那怎么办?两种思路。

维度 实时计算 预计算
计算时机 查询发起时 数据写入时(或定时)
查询速度 慢(秒级到分钟级) 快(毫秒级)
存储成本 低(只存原始数据) 高(存多份聚合结果)
数据新鲜度 实时 有延迟(取决于预计算频率)
适用场景 临时分析、小数据量 固定报表、大屏、高频查询

实时计算,说白了就是「每次查询都从头算一遍」。优点是灵活,你想怎么聚合都行。缺点是慢,数据量一大就崩。

预计算,就是「先把结果算好存起来」。优点是快,查的时候直接读结果。缺点是不灵活,维度组合一变,就得重新算。

我个人的习惯是:80% 的固定查询用预计算,20% 的临时分析走实时。别想着一个方案打天下。

3.3 空间换时间的核心思想

预聚合的本质,就是「空间换时间」。你想想看,原始数据 100GB,你预聚合之后可能变成 200GB、300GB。但查询时间从 30 秒降到了 10 毫秒。这笔账划不划算?

划算。但前提是你要算清楚账。

空间换时间的公式:

收益 = (原始查询耗时 - 预聚合查询耗时) × 查询次数

成本 = 预聚合存储空间 × 存储单价 + 预计算的计算资源

当 收益 > 成本 时,预聚合就是值得的。

举个例子。我之前的项目,原始数据 500GB,每天查询 1000 次,每次 30 秒。预聚合后存储变成 1.2TB,但查询降到 50 毫秒。

  • 每天节省的查询时间:1000 × (30 - 0.05) = 29950 秒 ≈ 8.3 小时
  • 增加的存储成本:700GB × 存储单价

算下来,一个月就能回本。而且用户体验的提升是无价的——业务方不用再等 30 秒了,点一下就能看到结果。

我曾经踩过的坑:一开始我把所有维度组合都预聚合了,结果存储膨胀了 10 倍,而且很多组合根本没人查。后来我改成只预聚合高频维度组合,存储只增加了 2 倍,效果一样好。记住:不是所有预聚合都有价值,只预聚合那些真正被查询的维度。

3.4 一张图看懂预聚合的核心逻辑

下面这张图,是我自己总结的预聚合决策流程。每次做预聚合设计前,我都会过一遍这个流程。

预聚合决策流程 原始数据(2亿行) 查询频率分析 高频查询? (>100次/天) 是 否 预聚合(空间换时间) 实时计算(临时分析) 选择聚合维度 存储聚合结果 直接查询原始数据 返回结果(慢) 核心原则:高频查询走预聚合,低频查询走实时计算,不要一刀切

你看,这个流程的核心就是「按需预聚合」。不是所有数据都要预聚合,也不是所有查询都要实时计算。根据查询频率做决策,才是最优解。

3.5 空间换时间的实战案例

最后分享一个我实际做过的案例。

某金融公司,交易流水表 5 亿行。业务方需要查「每个用户当月的交易总额、交易笔数、最大单笔交易」。每天查询量 5000 次。

原始方案:每次查询扫 5 亿行,平均 45 秒。数据库 CPU 常年 90% 以上。

我的方案:

  1. 按「用户 + 月份」做预聚合,每天凌晨跑一次
  2. 预聚合结果表只有 500 万行(因为每个用户每月一条记录)
  3. 查询直接走预聚合表,耗时从 45 秒降到 20 毫秒
  4. 存储从 200GB 增加到 250GB(只增加了 25%)

关键点:我选择了「用户 + 月份」这个粒度,而不是「用户 + 天」或「用户 + 小时」。为什么?因为业务方只查月度数据。粒度越粗,预聚合效果越好。如果你选「用户 + 天」,存储可能增加 30 倍,但查询速度提升有限。这就是「粒度选择」的艺术。

嗯,这一章的内容就到这里。记住:预聚合不是银弹,但用好了,它就是性能优化的核武器。核心就是三个字——算清楚。算清楚查询频率,算清楚存储成本,算清楚粒度选择。算清楚了,你就知道该不该用预聚合,该怎么用。