2. 数仓体系架构:经典三层架构、ETL与ELT、Lambda与Kappa

聊数仓,第一个绕不开的就是架构。我见过不少团队,一上来就急着建表、写ETL脚本,结果跑了大半年发现数据对不上、链路乱成一锅粥。说白了,架构就是数仓的骨架——骨架歪了,后面怎么填肉都别扭。

这一节,我把几个核心概念掰开揉碎讲清楚。你跟着我的思路走一遍,心里就有谱了。

2.1 经典三层架构:ODS、DW、DM

这是最传统、也是最成熟的一套分层方式。我最早做数仓时用的就是这套,到现在很多大厂的核心数仓依然在沿用。它把数据从源头到应用切成了三层:

层级 全称 核心职责 典型存储周期
ODS 操作数据存储 原样接入源系统数据,不做或少做清洗 近7天或全量
DW 数据仓库层 数据清洗、整合、建模(明细+轻度汇总) 历史全量
DM 数据集市层 面向业务主题的汇总数据,直接供报表/应用使用 按需保留

核心原则:每一层只依赖它的下一层,禁止跨层引用。ODS不能直接查DM,DM也不能跳过DW去读ODS。这条规矩我见过太多人打破,后果就是数据血缘乱成一锅粥。

举个例子。你在ODS层存的是业务库的原始订单表,字段名可能是order_id、create_time、status。到了DW层,你把它拆成订单事实表、客户维表、商品维表,做SCD2缓慢变化维。到了DM层,你只保留“每日订单金额汇总”这种宽表,直接给BI工具用。

这样做的好处很明显:每一层职责单一,出了问题容易定位。我曾经在一个项目里遇到过ODS层数据延迟,但DM层报表还在跑——因为DM依赖的是DW层前一天的数据,ODS的延迟根本没影响到它。这就是分层带来的解耦红利。

2.2 ETL与ELT的区别

这两个词看着像,但背后的架构思路完全不同。我直接给你画个对比:

维度 ETL ELT
处理顺序 先转换,再加载 先加载,再转换
计算位置 通常在ETL服务器或中间件 在目标数据库/数据湖内
适用场景 传统关系型数仓、数据量中等 大数据平台(Hadoop、Spark)、云数仓
灵活性 转换逻辑固定,变更成本高 数据先落地,后续按需转换
典型工具 Informatica、DataStage、Kettle dbt、Spark、Snowflake、BigQuery

我个人习惯这样判断:如果你的数据量在TB级以下,团队对SQL很熟,用ETL完全够用。但如果数据量到了PB级,或者你希望数据科学家能随时拿到原始数据做探索,那ELT更合适。

我的经验:ELT模式下,数据加载到目标系统后,通常先做一次轻量级的“格式统一”(比如把时间戳转成标准格式),这叫“schema-on-read”。真正的业务转换逻辑,等查询时再通过视图或dbt模型来执行。这样做的好处是——原始数据永远保留,不会因为转换逻辑写错而丢失。

嗯,这里要注意一个坑。ELT虽然灵活,但对目标系统的计算能力要求很高。我曾经在一个项目里把ETL全改成ELT,结果数据是先进来了,但后续的转换查询把集群跑挂了——因为没做分区和索引优化。所以ELT不是银弹,你得确保目标系统扛得住。

2.3 Lambda架构与Kappa架构

聊完分层和处理模式,咱们再往上看一层——整个数仓系统的架构选型。Lambda和Kappa是两种主流的实时数仓架构思路。

2.3.1 Lambda架构

Lambda架构把数据处理分成两条路:

  • 批处理层(Batch Layer):处理全量历史数据,产出准确的结果。延迟高,但数据完整。
  • 流处理层(Speed Layer):处理实时增量数据,产出近实时的结果。延迟低,但可能有误差。
  • 服务层(Serving Layer):合并批处理和流处理的结果,对外提供统一查询。

说白了,Lambda架构就是用两套代码维护同一套业务逻辑。批处理跑T-1的全量数据,流处理跑当天的增量数据,最后在服务层合并。

我曾经踩过的坑:Lambda架构最大的痛点是维护两套代码。批处理用Spark SQL写,流处理用Flink SQL写,同样的业务逻辑要写两遍。一旦业务规则变了,两边都得改,而且很容易出现批流结果不一致的情况。有一次我们批处理算出的GMV是1000万,流处理算出来是980万,差了20万——排查了两天才发现是流处理的时间窗口切分逻辑跟批处理不一样。

2.3.2 Kappa架构

Kappa架构的思路更激进——只用一套流处理引擎,处理所有数据。它把批处理看作流处理的一个特例:只要把历史数据重新“回放”一遍,流处理引擎就能产出跟批处理一样的结果。

Kappa架构的核心组件:

  • 消息队列(如Kafka):作为数据的统一入口和存储层,保留全量数据。
  • 流处理引擎(如Flink):消费消息队列中的数据,做实时计算。
  • 结果存储(如HBase、ClickHouse):存储计算结果,供查询。

你想想看,Kappa架构最大的好处是什么?一套代码,搞定所有。没有批流不一致的问题,运维也简单。但它的前提是——你的消息队列能存下全量历史数据,并且流处理引擎能扛住历史数据回放的压力。

选型建议:

  • 如果业务对实时性要求不高(分钟级甚至小时级),且团队熟悉批处理,Lambda架构更稳妥。
  • 如果业务要求秒级实时,且数据量巨大,团队有较强的流处理能力,Kappa架构更优雅。
  • 如果你们刚开始做实时数仓,我建议从Lambda起步,等流处理玩熟了再考虑向Kappa演进。

2.4 三种架构的对比总结

我把经典三层、Lambda、Kappa放在一起做个对比,方便你快速决策:

维度 经典三层 Lambda Kappa
核心思想 分层解耦 批流分离 批流一体
实时能力 弱(离线为主) 中(批+流两套) 强(纯流式)
代码维护成本 低 高(两套代码) 中(一套代码)
数据一致性 高 中(批流可能不一致) 高(同一引擎)
适用场景 传统BI报表 实时+离线混合 纯实时、数据湖

最后,我用一张图把这三者的关系串起来。你一看就明白:

数仓体系架构总览 数仓体系架构总览 经典三层架构 ODS 操作数据存储 DW 数据仓库层 DM 数据集市层 每层只依赖下一层 禁止跨层引用 Lambda 架构 数据源 批处理层 流处理层 服务层(合并结果) 两套代码维护同一业务逻辑 批流结果可能不一致 Kappa 架构 消息队列(Kafka) 流处理引擎(Flink) 一套代码处理所有数据 历史数据通过回放实现 Kappa 结果存储 结果存储(HBase/ClickHouse) 写入

这张图把三种架构放在一起对比。你看左边经典三层,数据是垂直流动的,一层接一层。中间Lambda,数据从源头分叉成两条路,最后合并。右边Kappa,数据走一条路,通过消息队列回放实现历史数据处理。

选哪种架构,没有标准答案。我个人的建议是:先搞清楚你的业务场景,再选架构,而不是反过来。很多团队一上来就追Lambda、Kappa,结果业务场景根本不需要实时,白白增加了复杂度。

一个小技巧:如果你不确定选哪种,可以先从经典三层+离线ETL做起。等业务提出实时需求了,再在DM层旁边加一个实时流处理管道,逐步演进到Lambda。这样风险最小,也最符合大多数公司的实际节奏。