【数据中台·3】数仓分了五层,三层在越权,分层分了个寂寞

这是【数据中台】系列第3篇。上篇讲指标体系踩坑,这篇讲分层越权。

架构文档一摞摞,ODS、DW、DWD、DWS、ST,每层职责写得清清楚楚。实际跑起来呢?ODS开始写CASE WHEN做业务映射,DW开始JOIN维度表,DWS把DWD整张表COPY过来改个名——五层架构,三层在干别人的活,分层分了个寂寞。

这篇用一张运单的真实流转,讲清楚越权是怎么发生的,以及怎么守住边界。


ODS层:一个CASE WHEN,口径锁死三个月

ODS层最常见的越权:写CASE WHEN做业务映射。

我见过一个项目,ODS层这么写:

-- ODS层直接做了产品类型映射
case 
  when product_type_code = 'RETURN' then '退件'
  when temperature_range_code = 'COLD' then '冷链'
  ...
end as product_type_name

三个月后,业务改了映射规则:冷链要拆成"冷藏"和"冷冻"。下游DW、DWD、DWS全依赖ODS这份数据,ODS已经覆盖写入了,历史数据改不回来。

结果:要么重刷三个月全量,要么口径从此锁死在ODS层。

ODS层只做一件事:异构数据源接入,原样保留。 格式标准化可以做(字段类型统一、时区转换、JSON解析),业务判断不能做。映射留给DWD,ODS只存原值。这是最便宜的防线——ODS不动业务逻辑,后面就不会连锁越权。


DW层:两层其实可以合并成一层

DW层干两件事:去重 + 时间轴重建。

去重是因为ODS层同一主键可能有多条记录(业务系统变更导致),DW层按主键保留最新版本。时间轴重建是因为ODS按modified_time拉增量,分析需要按waybill_time切分区。

最常见的越权:在DW层做维度JOIN。

-- ❌ DW层越权:开始JOIN维度表
select 
    a.waybill_no,
    b.site_name as send_site_name    -- 不该在这做
from dw.dw_waybill_detail_inc a
left join dim.dim_org b on a.send_org_code = b.site_code

后果:DWD层只剩"复制DW+改字段名"的活,两层合并。

但我更想说的是:ODS和DW做的事情其实很近,可以考虑合并。

  • ODS:异构数据源接入,原样保留,15天增量窗口
  • DW:去重+时间轴重建

两层之间没有业务逻辑差异,只是清洗程度不同。ODS就是个增量缓冲区,DW才是真正的贴源层。很多团队把两层合并成一层叫"贴源层",DW直接做接入+去重+标准化,一步到位。

能少一层就少一层,前提是合并后的DW守住边界:只做去重和标准化,不做维度JOIN,不做业务映射。


连锁反应:DWD顺手一聚合,后面全乱套了

分层最怕的不是一层越权,是连锁反应。

DWD层的本职是维度JOIN——把业务编码替换成维度表的名称、层级。但DWD发现上游DW已经把维度JOIN做了,自己没活干,就顺手加了COUNT/SUM:

-- DWD层顺手做了聚合
select send_site_code, count(waybill_no) as ewb_count
from dwd.dwd_ewb_basic_detail_inc
group by send_site_code

DWS层一看,DWD已经聚合了,自己还聚合个啥?直接COPY:

-- DWS层COPY DWD整表
insert overwrite table dws.dws_ewb_site_cargo_volume_basic_info
select * from dwd.dwd_ewb_basic_detail_inc  -- 没有聚合逻辑

ST层一看,DWS跟DWD一模一样,我还费劲从DWS拉干嘛?直接查DWD:

-- ST层绕过DWS,直接查DWD
select province_area_code, count(waybill_no) ewb_count
from dwd.dwd_ewb_basic_detail_inc    -- 不该查DWD
group by province_area_code

然后问题来了:DWS过滤了退件和作废,ST查DWD时忘了过滤,报表数字跟DWS出来的对不上。对数对到凌晨,最后发现是ST绕过了DWS。

连锁反应的根因:ODS做了映射→DW跟着做JOIN→DWD没活干就顺手聚合→DWS看到DWD已经聚合了就COPY→ST看到DWS跟DWD一样就直接查DWD。每一层的越权都是上一层逼出来的。守住一层,后面就不会连锁崩。


守住边界:三条禁令就够了

不需要厚厚的规范文档。三条禁令够了:

1. ODS层禁止CASE WHEN — 业务映射全部放DWD,ODS只存原值

2. DWD层禁止GROUP BY — 聚合全部放DWS,DWD只做维度JOIN

3. ST层禁止FROM DWD — ST只读DWS,需要明细查DWD,不需要绕过DWS

运单的完整流转就是守住这三条的结果:

ods.ods_waybill_detail
原值,15天增量

dw.dw_waybill_detail_inc
去重+时间轴,60天回刷

dwd.dwd_ewb_basic_detail_inc
维度JOIN+业务映射
不过滤不聚合

dws.dws_ewb_site_cargo_volume_basic_info
过滤退件+作废
按网点聚合

st.st_ewb_province_area_target_d
只读DWS
省区报表

MateBase / FinReport / DataEase

每层干自己的事,下游不回头,上游不越权。分层才有意义。


写在最后

分层这个词,听起来像架构师才操心的事。但分层出问题的时候,疼的不是架构师,是每天对数的你。

ODS写了业务逻辑,改口径要重刷全量;DW做了维度JOIN,DWD层变成空壳;DWS直接COPY DWD,ST回头查DWD——每一层的越权都在给下一层挖坑,最后坑全堆在你身上,对数对到凌晨。

守住边界不需要厚厚的规范文档,三条禁令就行:ODS禁止CASE WHEN,DWD禁止GROUP BY,ST禁止FROM DWD。能少一层就少一层,能少一条规则就少一条规则。

分层不是分五个schema就完了,是每层都在干自己的事。

更多推荐