2. 数据仓库分层设计总览
做数据仓库这些年,我见过不少团队一上来就建表、写ETL,三个月后整个项目变成一团乱麻。说白了,没有分层设计的数据仓库,就像没有抽屉的衣柜——东西都能塞进去,但想找出来就难了。
今天咱们聊聊分层这件事。我会把为什么分层、常见分几层、设计原则是什么,一次性讲清楚。
为什么要分层?三个核心价值
先说说分层的意义。你想想看,如果所有数据都堆在一层,会是什么场景?
- 业务变了,你得改所有ETL——牵一发动全身
- 数据重复计算——同样的聚合逻辑,三个团队各写一遍
- 出了问题找不到根因——数据血缘一团浆糊
分层就是为了解决这些问题。我归纳了三个核心价值:
1. 解耦:让各层各司其职
解耦是分层的首要目标。每一层只关心自己的事:
- ODS只管接入,不管业务逻辑
- DWD只管清洗,不管聚合
- DWS只管汇总,不管明细
- ADS只管输出,不管来源
我在项目中遇到过这样一个案例:业务方要求修改一个指标的计算口径。因为分层清晰,我们只改了DWS层的一个模型,下游ADS和报表完全不用动。如果没分层,那得从ODS到报表全改一遍,至少多花三天。
2. 复用:一次加工,多处使用
复用的价值体现在两个层面:
- 计算复用:DWD层的明细数据,可以被多个DWS模型共享
- 逻辑复用:DWS层的汇总指标,可以被多个ADS报表共用
举个例子。用户活跃度这个指标,在DWS层算一次,下游的日报、周报、月报、大屏都能直接引用。如果不分层,每个报表都得自己算一遍,不仅浪费计算资源,口径还可能不一致。
复用带来的直接收益:
- 存储成本降低30%-50%
- 计算资源节省40%以上
- 指标口径统一,业务方不再吵架
3. 清晰血缘:出了问题能追溯
数据血缘,说白了就是数据从哪里来、经过哪些处理、最终去了哪里。分层之后,血缘关系一目了然:
- ODS → DWD:字段级映射
- DWD → DWS:聚合逻辑
- DWS → ADS:指标定义
我曾经遇到一个线上事故:某核心报表数据突然翻倍。因为分层清晰,我们沿着血缘链路反向追踪,十分钟就定位到DWS层一个聚合逻辑被误改。如果没分层,光排查就得半天。
常见分层架构:四层模型
业界最成熟的分层架构是四层模型。我画了一张图,帮你快速理解:
下面我逐一说明每层的职责。
ODS层:数据接入层
ODS(Operational Data Store)是数据仓库的入口。它的核心职责就两个:
- 接入:把业务系统的数据原封不动搬过来
- 保留:保存历史快照,支持数据回溯
嗯,这里要注意:ODS层不做任何业务逻辑处理。我曾经见过有人在ODS层做数据清洗,结果源系统字段变了,清洗逻辑全崩。ODS就该保持原始状态,脏数据也要原样保留。
DWD层:明细数据层
DWD(Data Warehouse Detail)是数据仓库的核心。这一层做三件事:
- 清洗:去重、空值处理、格式标准化
- 维度退化:把维度属性退化到事实表
- 一致性维度:统一各系统的维度定义
举个例子。用户性别这个字段,CRM系统存的是"男/女",订单系统存的是"M/F"。在DWD层,我会统一转换成"male/female"的枚举值。这样下游用起来就省心了。
DWS层:汇总数据层
DWS(Data Warehouse Service)是面向分析的汇总层。它的核心价值是:
- 预计算:把高频使用的指标提前算好
- 宽表化:把多个维度的指标拼成一张宽表
- 公共化:提供统一的指标口径
我习惯在DWS层建两类表:一类是日汇总宽表,按天聚合所有核心指标;另一类是多维汇总表,支持按不同维度切片。这样ADS层查询时,基本不用做复杂计算。
ADS层:应用数据层
ADS(Application Data Store)是面向具体业务需求的输出层。这一层的特点是:
- 定制化:每个报表、每个应用都有自己的ADS表
- 轻量化:数据量小,查询快
- 易用性:业务方可以直接用,不需要懂数据仓库
一个典型的数据流转示例:
ODS: 订单表(原始数据)
↓
DWD: 订单明细表(清洗后,关联用户、商品维度)
↓
DWS: 日订单汇总表(按天、按品类聚合)
↓
ADS: 销售日报表(业务方直接使用)
分层设计原则:五个要点
讲了这么多理论,最后说说实操层面的设计原则。这些是我踩过坑之后总结出来的:
原则一:单向依赖,禁止反向
数据流向必须是单向的:ODS → DWD → DWS → ADS。禁止下层依赖上层,也禁止跨层依赖。比如DWS不能直接读ODS,ADS不能直接读DWD。
为什么?因为一旦出现反向依赖,数据血缘就乱了。我曾经在一个项目里发现,DWS层直接读了ODS的原始数据,结果DWD层改了清洗逻辑,DWS层的数据口径对不上了。排查了整整两天。
原则二:适度冗余,不要过度
分层设计允许数据冗余。比如用户维度信息,在DWD层和DWS层都可能存在。但冗余要适度:
- 该冗余的:高频使用的维度属性、公共指标
- 不该冗余的:低频字段、大字段(如用户头像URL)
我一般遵循"三倍原则":如果某个字段在三个以上场景被用到,就考虑冗余到上层。否则保持引用关系即可。
原则三:命名规范,一看就懂
表名、字段名必须有明确的命名规范。我推荐这种格式:
{层标识}_{主题域}_{业务实体}_{粒度}_{时间周期}
示例:
ods_trade_order_di -- ODS层,交易域,订单表,日增量
dwd_trade_order_di -- DWD层,交易域,订单明细,日增量
dws_trade_order_1d -- DWS层,交易域,订单汇总,近1天
ads_trade_daily_report -- ADS层,交易域,日报表
原则四:粒度一致,避免混淆
同一层内的表,粒度必须保持一致。比如DWD层的订单明细表,每行代表一个订单;DWS层的日汇总表,每行代表一个订单+一天。粒度不一致会导致数据对不上。
我见过最离谱的情况:DWS层一张表里,既有订单级数据,又有用户级数据。业务方查"用户平均订单数"时,直接拿用户数除以订单数,结果完全不对。
原则五:分层可扩展,预留空间
设计分层时,要预留扩展空间。比如:
- ODS层预留字段,应对源系统变更
- DWD层预留维度,应对新业务接入
- DWS层预留指标,应对新分析需求
我习惯在每个表里加5-10个预留字段,类型设为string。这样新需求来了,不用改表结构,直接往里填数据就行。
分层设计原则总结:
| 原则 | 核心要求 | 常见误区 |
|---|---|---|
| 单向依赖 | 数据流向不可逆 | 跨层直接访问 |
| 适度冗余 | 高频字段冗余,低频字段引用 | 全量冗余或零冗余 |
| 命名规范 | 层+主题+实体+粒度 | 随意命名、无规则 |
| 粒度一致 | 同层表粒度统一 | 混合粒度 |
| 可扩展 | 预留字段和空间 | 表结构写死 |