Lambda 架构详解:原理、三层结构与实战经验
Lambda 架构,说白了就是一套「既要又要」的解决方案。既要批处理的准确性,又要流处理的低延迟。我在 2016 年第一次接触这个架构时,觉得它有点笨重——为什么要把数据跑两遍?后来在实时数仓项目里被数据一致性问题折磨了三个月,才明白 Lambda 的良苦用心。
Lambda 架构的核心原理
Nathan Marz 提出 Lambda 架构时,核心思想就一句话:用批处理保证最终正确,用流处理保证实时可见。它把数据 pipeline 拆成三条路径:
- 批处理层(Batch Layer):全量数据重算,产出不可变的 master 数据集
- 速度层(Speed Layer):实时增量计算,补偿批处理的延迟
- 服务层(Serving Layer):合并批视图和实时视图,对外提供统一查询
你想想看,这三层各司其职,有点像公司的「财务部+前台+报表系统」。财务部月底才出总账(批处理),前台每天记流水(速度层),报表系统把两者合并给你看(服务层)。
核心公式:Query = function( BatchView ∪ RealTimeView )
用户看到的最终结果,是批处理视图和实时视图的合并。批处理负责修正,实时负责新鲜。
批处理层(Batch Layer)
批处理层是 Lambda 架构的「压舱石」。它的职责很简单:存储原始数据,定期全量重算。我习惯用 HDFS 或 S3 存原始日志,然后用 Spark 每天凌晨跑一次全量 ETL。
这里有个关键设计——数据不可变。原始数据一旦写入,永远不允许修改或删除。为什么?因为批处理层要保证「无论什么时候重算,结果都一样」。如果数据能改,那今天跑和明天跑的结果可能不同,整个架构的可靠性就崩塌了。
我的经验:批处理层不要只存「处理后的数据」,一定要保留原始日志。我曾经遇到一个需求,要回溯三个月前的用户行为路径,幸好原始数据还在,否则只能跟业务方说「抱歉,算不了」。
批处理层的典型技术栈:
| 组件 | 用途 | 我常用的版本 |
|---|---|---|
| HDFS / S3 | 原始数据存储 | HDFS 3.x / S3 Standard |
| Spark / Hive | 全量计算引擎 | Spark 3.x + Hive 3.x |
| Parquet / ORC | 列式存储格式 | Parquet + Snappy 压缩 |
速度层(Speed Layer)
速度层存在的唯一理由——批处理太慢了。批处理层跑一次全量可能要几个小时,但业务方要的是秒级延迟。速度层就是用来填补这个时间差的。
速度层只处理「批处理还没覆盖到的新数据」。比如批处理每天凌晨 2 点跑完昨天的全量,那速度层就负责处理今天 0 点到现在的实时数据。等明天批处理跑完,速度层的数据就可以丢弃了。
注意:速度层的结果是「近似正确」的。因为它只看到部分数据,无法做全局聚合。比如计算「今日 UV」,速度层只能给出近似值,精确值要等批处理层来修正。
速度层的典型实现:
- Flink:我最常用的实时计算引擎,支持 exactly-once 语义
- Kafka Streams:轻量级方案,适合简单的状态计算
- Spark Streaming:微批次模式,延迟在秒级到分钟级
举个实际例子。我在电商项目中用 Flink 做实时订单统计,每 10 秒输出一次「最近 5 分钟的订单金额」。这个值是不精确的——因为可能有延迟到达的订单。但业务方看实时大屏,要的就是「大概齐」,精确数据等明天批处理修正就行。
服务层(Serving Layer)
服务层是 Lambda 架构的「门面」。它负责把批处理层的全量结果和速度层的增量结果合并,对外提供统一的查询接口。
服务层需要支持:
- 低延迟查询:用户点开报表,不能等 10 秒才出数据
- 数据合并:批视图 + 实时视图 = 最终结果
- 高并发:可能同时有几百个查询请求
我常用的服务层组件是 Druid 和 Cassandra。Druid 适合 OLAP 场景,预聚合能力强;Cassandra 适合 KV 查询,写入吞吐高。
合并逻辑示例:
-- 批处理层产出:昨日之前的全量聚合
SELECT date, SUM(amount) AS total
FROM batch_order_summary
WHERE date < '2024-01-15'
GROUP BY date;
-- 速度层产出:今日的实时增量
SELECT '2024-01-15' AS date, SUM(amount) AS total
FROM realtime_order_stream
WHERE window_start >= '2024-01-15 00:00:00';
-- 服务层合并:UNION ALL 即可
SELECT date, total FROM batch_view
UNION ALL
SELECT date, total FROM realtime_view;
优缺点分析
Lambda 架构不是银弹。我用它做过三个项目,有成功的,也有踩坑的。下面是我的真实感受。
| 维度 | 优点 | 缺点 |
|---|---|---|
| 数据准确性 | 批处理保证最终一致,不会丢数据 | 实时视图和批视图可能不一致,需要合并逻辑 |
| 延迟 | 速度层提供秒级响应 | 批处理层延迟大(小时级) |
| 开发成本 | 批和流可以独立开发 | 同一逻辑要写两遍,维护成本翻倍 |
| 运维复杂度 | 各层职责清晰,问题定位快 | 需要维护两套 pipeline,资源消耗大 |
| 扩展性 | 批和流可以独立扩缩容 | 服务层合并逻辑可能成为瓶颈 |
我曾经踩过的坑:在速度层和批处理层使用不同的聚合逻辑。批处理用 Hive SQL,速度层用 Flink SQL,结果两个引擎对「去重计数」的实现有细微差异,导致合并后的数据对不上。后来我强制要求:批和流的业务逻辑必须用同一套代码生成,或者至少用同一套 UDF。
Lambda 架构最适合的场景:
- 对数据准确性要求极高(如金融、计费)
- 既有历史全量分析需求,又有实时监控需求
- 团队有足够的资源维护两套 pipeline
不适合的场景:
- 只需要实时,不需要历史回溯(用纯流处理即可)
- 只需要离线报表,不需要秒级延迟(用纯批处理即可)
- 团队小,维护两套代码吃力(考虑 Kappa 架构)
嗯,Lambda 架构就讲到这里。它虽然笨重,但在很多场景下仍然是「最稳」的选择。下一层我们聊聊它的进化版——Kappa 架构,看看怎么用一套 pipeline 解决所有问题。