【背景】 做了 8 年数据工程,最近半年在帮三个不同团队看 OLAP 链路,发现一个反复出现的"看似简单、其实没人说得清"的取舍:
业务方要一个运营看板,核心指标是「近 1 小时 / 近 24 小时 / 近 7 天」的 PV、UV、GMV、订单转化率。数据源是埋点 + 订单流,写入速率峰值约 4 万 events/s(Kafka 单 topic,3 分区)。业务方同时提了两个要求:
- 看板刷新间隔 ≤ 10s;
- 任意时刻看板数字与「如果当下直接扫原始表」的偏差 ≤ 0.5%(他们称之为"准实时")。
这两个要求单看都不难,但同时满足时,"预聚合"和"实时算"两条路线的取舍就开始变得很难拍。
【我看到的几种典型做法,分歧点都在这】
路线 A:纯实时算(OLAP 引擎直接扫明细)
- ClickHouse / StarRocks / DuckDB 直接查明细,靠列存 + 向量化扛。
- 优点:数据永远最新,逻辑只有一份,不用维护增量/回溯。
- 代价:P99 直接和扫描行数成正比。我们一个 7 天 GMV 查询,明细 2.8 亿行,ClickHouse 调优后 P50 是 380ms,P99 飙到 2.4s(碰上合并后台 IO 抖动)。
- 业务方第一次看到 2.4s 的 P99 就说"这不能叫实时"。
路线 B:物化视图 / 预聚合(rollup)
- 维护 1min / 1hour / 1day 三档 rollup,查询只读 rollup。
- 优点:P99 稳定在 40–80ms,扛 10s 刷新毫无压力。
- 代价:
- 数据新鲜度天然落后一个 rollup 周期(1min rollup → 数据最多晚 1 分钟);
- 业务方一旦换维度(比如加个"按 SKU 大类拆"),就要重跑历史,一次 4–6 小时;
- 回刷逻辑容易踩坑:我们有一次按"事件时间"分桶,结果跨天补数据时把昨天的 rollup 写脏了,对账花了 2 天才定位到是
toStartOfMinute(event_time)和toStartOfMinute(ingest_time)用混了。
路线 C:混合(明细 + rollup 双写,查询时降级)
- 默认查 rollup,rollup 没覆盖到(新维度 / 历史回刷中)自动 fallback 明细。
- 优点是体验最好,但代价是两条链路都要维护,rollup 的 freshness 监控、fallback 的触发条件、对账逻辑,加起来比单条路线多 30–50% 的运维量。
- 我个人的体感:这条路团队 ≥ 3 人全职投入数据平台才值得走,小团队走这条路最后一定是在"对账"上崩溃。
【我的倾向与理由】 我自己会按一个不太性感但很实用的阈值来切:
- 如果业务方能接受"5 分钟级"的延迟(绝大多数运营看板其实能接受)→ 走 B,rollup 周期 1min 或 5min,把"准实时"定义成"准 5 分钟",写进 SLO。
- 如果业务方坚持 ≤ 10s 刷新 → 走 C,但我会先逼他们把"偏差 ≤ 0.5%"这条砍掉或改成 P95 口径,因为 10s 刷新 + 0.5% 偏差 + 维度可任意换,这三个一起要,等于让我同时做 Kafka 精确一次 + 物化视图 + 实时维度扩展,三条战线一起打,最后必挂一条。
- 如果数据量 < 5000 万行 / 查询维度 ≤ 3 个 → 直接 A,别造 rollup,ClickHouse 单表扛得住,省下来的运维成本远超那点 P99 抖动。
我的核心判断是:"实时"这个词在业务方嘴里是个形容词,在工程嘴里是个成本函数。 把"实时"翻译成"最大可接受延迟 × 最大可接受查询 P99 × 最大可接受维度变化频率"这三个数,方案就自己浮出来了。
【想听大家在什么场景下怎么选】
- 你们团队是怎么把"实时"翻译成这三个数的?是写进 SLO 还是只存在 PM 的脑子里?
- 走 B(rollup)的团队:你们怎么处理"业务方临时加维度"?是重跑历史、还是干脆拒绝?
- 走 C(混合)的团队:fallback 的触发条件你们是怎么定的?rollup 数据延迟超过多少秒就降级?
- 有没有人试过不预聚合、靠 ClickHouse 的
materialize_skip_unused_indices+ 投影这种"半预聚合"路线?效果如何,P99 和纯明细差多少? - 最刺头的一个:如果业务方坚持"10s 刷新 + 0.5% 偏差 + 维度随时换",你会怎么跟他对线?(这条纯想听故事,我已经被 PM 怼过两次了。)
0赞
0收藏
0楼层