考察点
这道题考的是你能不能把离线数仓的分层思想迁移到实时场景,并且知道实时下的特殊约束:层数不能深、状态要管理、维表关联方式不同。面试官想听你画出一条完整链路并讲清每层的存储和技术选型理由。追问常落在「为什么不分五层」「维表 join 怎么做」「一致性怎么保证」。
参考答案
分层:思路沿用,层数压浅
实时数仓沿用离线的分层思想,但层数必须比离线浅——每多一层就多一次 Kafka 往返和一次 checkpoint 状态,端到端延迟叠加、运维对象翻倍。生产上常见三到四层:
ODS(原始层):业务库 binlog(Canal/Debezium 采集 MySQL)和埋点日志(Flume/日志服务)原样进 Kafka,按主题划分,不做加工。这一层的意义是解耦和可回放——下游作业挂了、逻辑改了,从 Kafka 位点重追即可。
DWD(明细层):Flink 消费 ODS,做清洗(过滤脏数据、字段标准化)、维度退化(关联维表补上商品类目、用户属性)、多流合并(订单流 join 支付流),产出事实明细写回 Kafka,或写入 Paimon/Hudi 湖表供下游批流两用。
DWS(汇总层):Flink 按业务主题做轻度汇总,比如「店铺-分钟粒度成交金额」「用户-日粒度行为汇总」,结果是持续更新的流。这一层是复用的关键,避免每个报表都自己从明细重算。
ADS(应用层):面向具体场景的输出。看板类写 ClickHouse/Doris/StarRocks 供 OLAP 查询;点查类(用户实时画像、风控特征)写 Redis/HBase;需要对接业务的直接推 Kafka 或写 MySQL。
技术选型及理由
| 层/环节 | 主流选型 | 理由 |
|---|---|---|
| 消息/存储 | Kafka | 生态成熟、吞吐高、可回放;Paimon 正在部分场景替代它做流式存储 |
| 计算引擎 | Flink | 状态管理、Event Time、Exactly-Once 语义最完整,流批一体 |
| 明细/汇总存储 | ClickHouse、Doris、StarRocks | 列存 OLAP,亚秒查询;Doris/StarRocks 的主键模型支持 upsert,适合 CDC 场景 |
| 点查存储 | Redis、HBase | 毫秒级 KV 查询,维表关联和用户画像的标配 |
| 维表 | MySQL/HBase + 旁路缓存 | Flink lookup join 直查压力大,加 LRU 缓存或全量加载到内存 |
实时特有的三个工程点
维表关联是实时比离线麻烦的地方。离线 join 维表是免费的,实时要逐条 lookup:Flink 的 lookup join 配合本地缓存(比如 Caffeine 缓存热点维度、异步 IO 防阻塞)是标配;维度变化要考虑时点语义——订单应该关联下单时刻的维度快照,严格场景用 Temporal Join,多数场景容忍近似。
一致性:链路通常是 at-least-once + sink 幂等或两阶段提交。Kafka 到 Kafka 用 Flink checkpoint + 事务写出做到 Exactly-Once;写 CK 这类不支持事务的引擎,靠下游按主键去重(ReplacingMergeTree 或 Doris 主键模型)兜底。
延迟与成本的平衡:checkpoint 间隔、微批大小(mini-batch)直接决定延迟和资源消耗,看板场景 1 分钟级足够,没必要追秒级——延迟每降一个量级,成本可能翻几倍。
可能的追问
- 为什么实时不宜分五层六层? 每层之间要过 Kafka 并维护一份计算状态,层数多了端到端延迟线性叠加,任意一环抖动都会放大到下游;而且中间层都是流,出问题排查链路长。三到四层是延迟、复用和可维护性的平衡点。
- 双流 join 的状态会不会爆炸? 会。订单 join 支付要设状态 TTL(比如支付发生在下单后 1 天内,TTL 设 25 小时),超时的未匹配数据进侧输出流兜底,不能让状态无限攒。
- 实时和离线结果不一致怎么办? 架构上让离线 T+1 结果作为修正层覆盖实时结果(Lambda 思路),实时负责时效、离线负责准确;日常用小时级流批对账监控偏差率,超阈值告警。