数据仓库分层架构:从零搭建 ODS、DWD、DWS 到 ADS
AI摘要
本文详细介绍了数据仓库的分层架构,包括ODS(贴源层)、DWD(明细层)、DWS(汇总层)和ADS(应用层),并解释了各层的职责、命名规范和数据流转的全链路示例。分层架构有助于提高数据的可追溯性、可复用性、解耦合性和易维护性。
为什么你的报表总是对不上?从数据分层说起
什么是数据仓库分层
数据仓库(Data Warehouse)不是把数据堆在一起,而是按层次组织,让每一层只干一件事。
这样做的好处很实际:数据出问题时,能快速定位是哪一层的锅;中间层算好的结果可以被多个下游复用;源系统改了字段,只需要改最底层,上层不用动;每层职责清晰,改动的影响范围可控。
标准分层架构
主流的数据仓库分层方案如下:
业务系统 (OLTP)
↓
ODS(贴源层)
↓
DWD(明细层)
↓
DWS(汇总层)
↓
ADS(应用层)
↓
报表 / BI / 接口ODS:贴源层(Operational Data Store)
ODS 是数据进入仓库的第一站。从业务系统(MySQL、PostgreSQL、日志等)同步过来,原样保存,不加任何业务逻辑。按天分区,保留历史快照,顺带做数据质量检查(空值、格式异常这类)。
命名通常以 ods_ 开头,带上源系统名和表名:
ods_mysql_user_info_di # di = daily increment,日增量
ods_mysql_order_di
ods_log_app_click_di有几点容易踩坑:ODS 这层只管存,别在这里做业务逻辑;源系统如果有物理删除,ODS 要用软删除记下来;字段名保持原样,改名的事留到 DWD 做。
DWD:明细层(Data Warehouse Detail)
DWD 是数据清洗和整合的核心。ODS 的原始数据经过清洗、标准化、关联维度表之后,在这里变成干净的、面向业务过程的明细数据。具体做的事包括:去脏数据、空值处理、类型转换、统一时区编码和状态含义、把 ID 关联成有意义的字段(比如 user_id 关联出 city_name),以及按业务过程拆分或合并表。
命名以 dwd_ 开头,描述业务过程:
dwd_order_detail_di # 订单明细
dwd_user_behavior_click_di # 用户点击行为明细
dwd_pay_success_di # 支付成功明细示例
ODS 原始订单数据:
-- ods_mysql_order_di
order_id | user_id | status | create_time | amount
1001 | 888 | 2 | 2026-02-27 10:00:00 | 99.00DWD 清洗后:
-- dwd_order_detail_di
order_id | user_id | status_name | create_date | amount | city_name
1001 | 888 | 已付款 | 2026-02-27 | 99.00 | 上海关键点:
status=2转成了可读的已付款create_time拆出了create_date用于分区- 通过 user_id 关联用户维度表,补充了
city_name
维度表 vs 事实表
DWD 里有两类表:
| 类型 | 说明 | 示例 |
|---|---|---|
| 事实表(Fact) | 记录业务过程,有度量值 | 订单、支付、点击 |
| 维度表(Dimension) | 描述业务实体,相对稳定 | 用户、商品、地区 |
维度表通常以 dim_ 命名:
dim_user_info_full # full = 全量快照
dim_product_info_full
dim_region_fullDWS:汇总层(Data Warehouse Summary)
DWS 在 DWD 明细基础上做聚合汇总,按主题域和时间粒度生成宽表。这层是报表计算的主要数据来源。按天/周/月聚合统计指标,按业务主题构建宽表(用户域、商品域、地区域),提高下游查询效率,避免重复计算。
命名以 dws_ 开头,体现主题域 + 时间粒度:
dws_user_order_1d # 用户粒度,订单,最近1天
dws_product_sale_1d # 商品粒度,销售,最近1天
dws_region_gmv_1d # 地区粒度,GMV,最近1天举个例子,用户日汇总宽表长这样:
-- dws_user_order_1d
user_id | dt | order_count | pay_amount | avg_amount | city_name
888 | 2026-02-27 | 3 | 299.00 | 99.67 | 上海ADS:应用层(Application Data Store)
ADS 是最终结果表,直接给报表、BI 工具、接口查询用。按具体业务场景定制,结构贴近前端使用,通常是聚合度最高的一层。
命名以 ads_ 开头,体现具体业务报表:
ads_daily_gmv_report # 日GMV报表
ads_user_retention_report # 用户留存报表
ads_new_user_funnel # 新用户转化漏斗比如日 GMV 报表:
-- ads_daily_gmv_report
dt | gmv | order_count | pay_user_count | avg_order_amount
2026-02-27 | 10000.00 | 320 | 280 | 31.25数据流转全链路示例
以"统计昨日各城市 GMV"为例,完整链路如下:
1. ODS 入仓
-- 从 MySQL 同步原始订单
INSERT INTO ods_mysql_order_di
SELECT * FROM mysql_source.t_order WHERE date(create_time) = '2026-02-27';2. DWD 清洗
-- 清洗、标准化、关联维度
INSERT INTO dwd_order_detail_di
SELECT
o.order_id,
o.user_id,
CASE o.status WHEN 2 THEN '已付款' WHEN 3 THEN '已退款' END AS status_name,
o.amount,
u.city_name,
DATE(o.create_time) AS dt
FROM ods_mysql_order_di o
LEFT JOIN dim_user_info_full u ON o.user_id = u.user_id
WHERE o.dt = '2026-02-27' AND o.status = 2;3. DWS 汇总
-- 按城市聚合
INSERT INTO dws_region_gmv_1d
SELECT
city_name,
dt,
SUM(amount) AS gmv,
COUNT(1) AS order_count
FROM dwd_order_detail_di
WHERE dt = '2026-02-27'
GROUP BY city_name, dt;4. ADS 输出
-- 生成报表数据
INSERT INTO ads_city_gmv_report
SELECT city_name, dt, gmv, order_count
FROM dws_region_gmv_1d
WHERE dt = '2026-02-27'
ORDER BY gmv DESC;常见问题
分层一定要这么多吗?
小团队数据量不大时,可以简化为三层:ODS → DWD → ADS,跳过 DWS。但随着业务复杂度增加,DWS 能显著减少重复计算。
DWD 和 DWS 的边界在哪?
DWD 有明细行,一行代表一个业务事件(一笔订单、一次点击);DWS 已经聚合过了,一行代表一个统计周期的汇总结果。
维度表放哪层?
维度表通常单独放在 DIM 层或归入 DWD 层管理,不参与 ODS→DWS 的加工链路,供各层 JOIN 使用。
数据怎么跑?
分层数据通常用调度工具按依赖顺序跑任务,常见工具:
| 工具 | 特点 |
|---|---|
| Apache DolphinScheduler | 开源,国内用得多 |
| Apache Airflow | 功能全,社区大 |
| DataWorks | 阿里云托管,配合 MaxCompute |
| xxl-job | 轻量,Java 技术栈常用 |
总结
| 层次 | 英文名 | 核心职责 | 数据粒度 |
|---|---|---|---|
| ODS | Operational Data Store | 原样同步源数据 | 原始行 |
| DIM | Dimension | 维度数据管理 | 实体快照 |
| DWD | Data Warehouse Detail | 清洗标准化 | 业务事件行 |
| DWS | Data Warehouse Summary | 主题聚合宽表 | 统计周期行 |
| ADS | Application Data Store | 业务报表输出 | 指标结果行 |
搭数据仓库不难,难的是把每层的职责想清楚。先定好分层,再落技术选型,比一上来就堆 SQL 要稳得多。