09.数仓分层的常见误区:为什么别分太多层、ADS 和 DWS 怎么分
分层本身不复杂,复杂的是「分得对不对」。这篇文章盘点数仓分层最常见的六大误区,以及判断分层是否合理的方法。
上一篇文章讲了 数仓分层详解 里每层的职责。但知道每层干什么,不代表能分对。实际项目里,分层最容易犯的错,往往不是「不会分」,而是「分过头」或「分了不守」。
误区一:分层过多(过度设计)
有些团队一上来就分 7、8 层:ODS → STD → DWD → DWB → DWS → DM → ADS,还美其名曰「更细粒度、更灵活」。
真相是:每多一层,就多一层存储、多一层调度、多一层维护,还多一层「数据到底经过了几手」的困惑。
| 层级数 | 典型后果 |
|---|---|
| 3 层(精简) | 灵活,但公共逻辑可能沉淀不足 |
| 4-5 层(主流) | 平衡,绝大多数场景够用 |
| 6 层以上 | 维护成本陡增,数据血缘复杂,容易「为分层而分层」 |
判断标准:如果某两层之间找不到「明确的加工职责差异」,就可以合并。ODS/DWD/DWS/ADS 四层 + DIM 维度层,是经过大厂验证的主流结构,别轻易加层。
误区二:上层穿透下层
「ADS 直接读 ODS」 是最常见的分层失守。
❌ 错误:
ADS 报表 ←── 直接读 ODS(绕过 DWD 的清洗)
✅ 正确:
ADS ←── DWS ←── DWD ←── ODS(逐层依赖)
为什么不能穿透:穿透意味着绕过了 DWD 的清洗、标准化逻辑。源头改一个字段类型,你的 ADS 毫无感知地产出脏数据。这对应 数据仓库建模原则 里的「上层不穿透下层」。
分层约束的是依赖方向,不是可有可无的规范。一旦允许穿透,分层就形同虚设。
误区三:DWS 和 ADS 职责不清(重点)
这是被问得最多、也最容易错的地方。
| 维度 | DWS(汇总层) | ADS(应用层) |
|---|---|---|
| 服务对象 | 公共、多应用复用 | 具体应用/报表 |
| 内容 | 主题汇总宽表 | 定制化结果 |
| 是否含报表逻辑 | 否 | 是 |
| 例子 | 用户日汇总宽表 | 大盘 GMV 日报 |
判断口诀:
- 「多个应用都会用」→ 放 DWS(如用户日下单数、日支付金额,做留存、做画像、做看板都要用)
- 「只有某一个报表用」→ 放 ADS(如「上周 GMV 环比」这个具体的报表口径)
-- DWS:公共指标,多处复用(不该带「环比」这种具体报表口径)
SELECT user_id, SUM(order_amount) AS order_amount_1d
FROM dwd_trd_order_item
WHERE dt = '${biz_date}'
GROUP BY user_id;
-- ADS:具体报表口径(环比、同比这种「怎么看」的逻辑在这里算)
SELECT
dt,
gmv,
LAG(gmv, 7) OVER (ORDER BY dt) AS gmv_last_week, -- 环比口径
(gmv - LAG(gmv, 7) OVER (ORDER BY dt)) / LAG(gmv, 7) OVER (ORDER BY dt) AS gmv_wow_rate
FROM dws_trd_order_1d;
核心原则:DWS 是「事实的公共汇总」,ADS 是「如何看这些事实」。别把「怎么看」的逻辑塞进 DWS,否则 DWS 会被某张报表绑架。
误区四:公共逻辑不下沉
同一段清洗逻辑在三个地方重复出现,还不下沉到中间层:
-- ❌ 反模式:每个 ADS 都自己 JOIN 维度、自己清洗
-- ads_a.sql
SELECT u.city, SUM(o.amount)
FROM ods_order o JOIN ods_user u ON o.user_id = u.user_id
WHERE u.city IS NOT NULL AND o.status = 'paid' ...;
-- ads_b.sql ← 同样的 JOIN + 清洗又写一遍
SELECT u.city, COUNT(*)
FROM ods_order o JOIN ods_user u ON o.user_id = u.user_id
WHERE u.city IS NOT NULL AND o.status = 'paid' ...;
正确做法:把这套逻辑下沉到 DWD/DWS,ADS 只引用结果。
判断标准:同一段逻辑出现三次以上,就该下沉。但也别过度下沉——每多一张公共表,改动成本就多一分,这是 数据仓库建模原则 里的「公共逻辑下沉,个性逻辑上浮」。
误区五:每层都全量重算
「为了简单,每层每天都全量重算」——数据量小的时候没问题,一旦上 TB 级就是灾难。
| 层 | 正确的处理方式 |
|---|---|
| ODS | 增量或全量同步(看源系统) |
| DWD | 按天增量(只处理当天数据) |
| DWS | 按天重算当天分区(INSERT OVERWRITE 单分区) |
| ADS | 按天重算,读 DWS 结果 |
-- ✅ 正确的增量:只重算当天分区,而不是全表
INSERT OVERWRITE TABLE dws_usr_user_1d PARTITION (dt = '${biz_date}')
SELECT user_id, SUM(order_amount) ...
FROM dwd_trd_order_item
WHERE dt = '${biz_date}'
GROUP BY user_id;
全量重算不仅浪费计算,还容易因历史数据变化导致「今天和昨天跑出来的历史数对不上」。
误区六:分层形同虚设(分了不守)
最隐蔽的误区:层建了,命名也对,但实际依赖乱成一团。
表现:
- 表名叫
dws_,实际上在做 ADS 的报表逻辑 dwd_表里塞满了汇总字段- 血缘图里 DWD、DWS、ADS 互相引用,形成「网状」而不是「分层」
判断分层是否形同虚设:画出血缘图,看依赖关系是不是单向、分层、无环。如果发现 ADS 被 DWD 反向依赖,说明职责已经乱了。
判断分层是否合理的三个问题
- 改一个源字段,影响范围可控吗? 应该只波及 ODS + DWD,而不是全链路
- 一段公共逻辑,只出现一次吗? 应该只在下沉的层出现一次
- 每张表,说得出它属于哪一层、什么粒度吗? 说不出的表,就是分层没做好
小结
| 误区 | 一句话纠正 |
|---|---|
| 分层过多 | 4-5 层够用,别为分层而分层 |
| 上层穿透 | 只允许逐层依赖,禁止 ADS 读 ODS |
| DWS/ADS 不清 | DWS 公共汇总,ADS 定制结果 |
| 逻辑不下沉 | 三次以上就下沉 |
| 每层全量 | 按天增量/单分区重算 |
| 形同虚设 | 血缘必须单向分层无环 |
分层的目的不是「看起来专业」,而是**「让每一层只做一件事,且只做一次」**。守住这一点,上面的误区自然就避开了。
📌 本文是 dwsql.com 数仓建模方法论系列「数仓分层与指标体系」第 2 篇。上篇见 数仓分层详解,下篇讲指标体系设计,见 指标体系设计。
「数据仓库技术」文章同步更新,不错过每一篇干货

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

