08.数仓分层:ODS/DWD/DWS/ADS/DIM
数仓分层是数据仓库最基础也最容易「照猫画虎」的设计。本文讲清每一层到底干什么、为什么要这么分,以及分层后每张表该怎么设计。
很多人搭数仓,第一步就是套一个 ODS/DWD/DWS/ADS 的分层模板,但被追问「DWD 和 DWS 到底有什么区别」「ADS 能不能直接读 ODS」时,就答不上来了。
分层不是形式主义。分对了,数据可复用、口径可统一、出了问题能快速定位;分错了,就是给自己挖一堆维护的坑。
一、为什么要分层:分层的根本目的
在讲每层之前,先搞清楚「为什么非要分层」这个根本问题。
| 目的 | 说明 | 不分层的后果 |
|---|---|---|
| 解耦 | 上层不依赖下层的具体实现 | 源表改个字段,所有下游全炸 |
| 复用 | 清洗/加工逻辑沉淀在中间层 | 同样的 JOIN 逻辑写几十遍 |
| 口径统一 | 同一指标只在固定层算一次 | 活跃用户 3 个版本,报表对不上 |
| 数据质量 | 脏数据在底层拦截,不进上层 | 脏数据直接喂给报表 |
| 可追溯 | 出问题能逐层定位 | 数万行 SQL 无从查起 |
一句话:分层是为了「让每一层只做一件事,且只做一次」。这是 数据仓库建模原则 里「公共逻辑下沉」的架构化表达。
二、经典分层架构:一张图看清
主流数仓(阿里/美团/字节等)基本都采用「四层 + 一个维度层」的结构:
┌─────────────────────────────────────────────────────────┐
│ ADS 应用数据服务层 │
│ 「面向具体应用/产品/报表的定制结果」 │
│ 例:大盘 GMV 日报、用户留存看板、指标看板 │
├─────────────────────────────────────────────────────────┤
│ DWS 汇总数据层(主题宽表) │
│ 「按主题轻度汇总,形成宽表,服务公共指标」 │
│ 例:用户日汇总宽表、商品日汇总、订单日汇总 │
├─────────────────────────────────────────────────────────┤
│ DWD 明细数据层 │
│ 「清洗、标准化后的明细数据,贴近业务过程」 │
│ 例:订单明细、支付明细、浏览明细(含维度退化字段) │
├─────────────────────────────────────────────────────────┤
│ ODS 操作数据层(贴源层) │
│ 「与源系统几乎一致,只做最小加工」 │
│ 例:业务库订单表原样同步 │
└─────────────────────────────────────────────────────────┘
▲
DIM 维度层(横向贯穿)
用户、商品、地区、时间等维度表
数据流向:ODS → DWD → DWS → ADS,逐层加工;DIM 层是「公共维度」,横向被 DWD/DWS/ADS 引用。
三、ODS:贴源层,只做「原样落地」
3.1 职责
ODS(Operational Data Store)是数据进仓的第一站,目标是把源系统的数据「原样」搬进来,尽量不做加工。
3.2 设计规范
- 结构与源表几乎一致,只加少量管理字段(如
etl_time入仓时间、dt分区) - 只做最小清洗:去空格、补默认值,不做业务逻辑
- 保留全量历史,通常按天分区,全量或增量同步
-- ODS 层:订单表原样同步,只加分区字段
CREATE TABLE ods_order (
order_id STRING COMMENT '订单号',
user_id STRING COMMENT '用户ID',
amount DECIMAL(18,2) COMMENT '订单金额',
status STRING COMMENT '订单状态',
etl_time STRING COMMENT '入仓时间'
) COMMENT '操作数据层-订单表'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;
ODS 的核心价值是**「保留现场」**:不管后面怎么加工,原始数据都在,出问题能回溯到源。
四、DWD:明细层,做「清洗 + 标准化」
4.1 职责
DWD(Data Warehouse Detail)是清洗、标准化后的明细数据,贴近业务过程,是数仓真正的「事实明细」。
4.2 设计规范
- 按业务过程建模,一张表对应一个业务过程(下单、支付、退款分开建)
- 做字段标准化:类型统一、枚举码表化、空值语义化
- 退化维度下沉:订单号、发票号这类无属性维度直接放明细层
- 遵循维度建模,保留事实表的粒度,见 事实表完全指南
-- DWD 层:下单明细,清洗 + 标准化 + 退化维度
CREATE TABLE dwd_trd_order_item (
order_item_id STRING COMMENT '订单明细ID',
order_no STRING COMMENT '订单号(退化维度)',
user_id STRING COMMENT '用户ID',
product_id STRING COMMENT '商品ID',
shop_id STRING COMMENT '店铺ID',
region_id STRING COMMENT '地区ID',
order_amount DECIMAL(18,2) COMMENT '下单金额',
item_qty INT COMMENT '购买数量'
) COMMENT '交易域-下单明细层'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;
五、DWS:汇总层,做「主题宽表」
5.1 职责
DWS(Data Warehouse Summary)是按主题轻度汇总的宽表,把常用的维度预 JOIN 进来,服务公共指标,避免每个应用都重复算一遍。
5.2 设计规范
- 按主题聚合:用户、商品、订单、流量等主题各建一套汇总表
- 形成宽表:把高频维度打进来,下游查询免 JOIN,见 宽表设计全指南
- 保留可复用的公共指标,但不做面向具体报表的定制
-- DWS 层:用户日汇总宽表(预 JOIN 用户维度 + 预聚合行为)
CREATE TABLE dws_usr_user_1d (
user_id STRING COMMENT '用户ID',
city STRING COMMENT '所在城市(维度冗余)',
level STRING COMMENT '会员等级(维度冗余)',
order_cnt_1d BIGINT COMMENT '当日下单数',
order_amount_1d DECIMAL(18,2) COMMENT '当日下单金额',
pay_cnt_1d BIGINT COMMENT '当日支付数'
) COMMENT '用户域-用户日汇总宽表'
PARTITIONED BY (dt STRING)
STORED AS PARQUET;
六、ADS:应用层,做「定制结果」
6.1 职责
ADS(Application Data Service)是面向具体应用、产品、报表的定制结果,是分层的最顶层,直接给看板、接口、业务方消费。
6.2 设计规范
- 面向具体业务场景:大盘日报、留存看板、某个专题的指标
- 可以直接读取 DWD/DWS,但不应穿透到 ODS
- 口径必须可落地为「表 + 字段 + 过滤条件 + 聚合方式」
-- ADS 层:大盘 GMV 日报(面向具体报表)
CREATE TABLE ads_trd_gmv_daily (
dt STRING COMMENT '日期',
gmv DECIMAL(18,2) COMMENT '成交总额',
order_cnt BIGINT COMMENT '订单数',
pay_cnt BIGINT COMMENT '支付订单数',
pay_rate DECIMAL(9,4) COMMENT '支付转化率'
) COMMENT '应用层-大盘GMV日报';
七、DIM:维度层,做「公共一致性维度」
7.1 职责
DIM(Dimension)层存放跨业务共享的一致性维度:用户、商品、地区、时间等。它是「横向贯穿」的一层,被各层事实表引用。
7.2 设计规范
- 一致性维度:被多张事实表共享的维度必须统一维护,见 维度建模入门:星型 vs 雪花 vs 星座 的「一致性维度」
- 处理缓慢变化:维度属性变化用 SCD 策略,见 SCD 缓慢变化维详解
- 时间维度预生成:日期维表通常预生成未来几年
-- DIM 层:用户维度(拉链表,保留历史)
CREATE TABLE dim_user_zip (
user_id STRING COMMENT '用户ID',
city STRING COMMENT '所在城市',
level STRING COMMENT '会员等级',
start_date STRING COMMENT '生效日期',
end_date STRING COMMENT '失效日期,9999-12-31 表示至今有效',
is_current INT COMMENT '是否当前有效'
) COMMENT '维度层-用户拉链表';
八、分层命名规范(一图速查)
国内数仓普遍按「层级前缀 + 主题域 + 主体 + 粒度 + 频率」命名:
| 层 | 前缀 | 示例 | 含义 |
|---|---|---|---|
| ODS | ods | ods_order | 贴源订单表 |
| DIM | dim | dim_user_zip | 用户维度拉链表 |
| DWD | dwd | dwd_trd_order_item | 交易域下单明细 |
| DWS | dws | dws_usr_user_1d | 用户域日汇总 |
| ADS | ads | ads_trd_gmv_daily | 应用层大盘日报 |
看到表名就能判断它属于哪一层、什么主题、什么粒度——这是 数据仓库建模原则 里「命名是契约」的落地。
九、常见坑点
- 分层过多——有的团队分 7、8 层,每层都有维护成本,最终谁都不清楚数据经过了什么。详见 数仓分层的常见误区
- 上层穿透下层——ADS 直接读 ODS,绕过清洗逻辑,源表一改就出脏数据
- DWS 和 ADS 职责不清——DWS 是「公共汇总」,ADS 是「定制结果」,别把报表逻辑塞进 DWS
- ODS 做业务加工——ODS 应该「原样」,加工放 DWD,否则「现场」被破坏
- 每层都全量——应该分层增量,明细层按天增量、汇总层按天重算,避免无谓的全量扫描
十、小结
| 层 | 一句话 |
|---|---|
| ODS | 原样落地,保留现场 |
| DWD | 清洗 + 标准化,贴业务过程 |
| DWS | 主题汇总,形成宽表 |
| ADS | 面向应用,定制结果 |
| DIM | 公共一致性维度,横向贯穿 |
分层的本质是**「每层只做一件事,且只做一次」**。分得对不对,判断标准就一个:改一个源字段,能不能只动 ODS 和 DWD,而不波及 ADS?
📌 本文是 dwsql.com 数仓建模方法论系列「数仓分层与指标体系」第 1 篇。下一篇讲分层的常见误区,见 数仓分层的常见误区。
「数据仓库技术」文章同步更新,不错过每一篇干货

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

