跳到主要内容

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 反向依赖,说明职责已经乱了。


判断分层是否合理的三个问题​

  1. 改一个源字段,影响范围可控吗? 应该只波及 ODS + DWD,而不是全链路
  2. 一段公共逻辑,只出现一次吗? 应该只在下沉的层出现一次
  3. 每张表,说得出它属于哪一层、什么粒度吗? 说不出的表,就是分层没做好

小结​

误区一句话纠正
分层过多4-5 层够用,别为分层而分层
上层穿透只允许逐层依赖,禁止 ADS 读 ODS
DWS/ADS 不清DWS 公共汇总,ADS 定制结果
逻辑不下沉三次以上就下沉
每层全量按天增量/单分区重算
形同虚设血缘必须单向分层无环

分层的目的不是「看起来专业」,而是**「让每一层只做一件事,且只做一次」**。守住这一点,上面的误区自然就避开了。


📌 本文是 dwsql.com 数仓建模方法论系列「数仓分层与指标体系」第 2 篇。上篇见 数仓分层详解,下篇讲指标体系设计,见 指标体系设计。

📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

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

交流微信二维码

你可能还想看