从原始日志到业务报表:ODS、DWD、DWS、ADS 到底在分什么层

很多人第一次接触数据仓库时,最容易被一堆缩写劝退:ODS、DWD、DWS、ADS、DIM、STG、TMP、DM……看起来像一套黑话系统。

但这些缩写背后真正要解决的问题并不复杂:公司里的数据从业务系统、日志、埋点、消息队列流进来以后,不能所有人都直接查原始表,也不能每个报表都从头写一遍清洗和聚合逻辑

所以数仓会把数据加工过程拆成不同层级。每一层负责一类事情:有的保存原始数据,有的做清洗和标准化,有的沉淀公共汇总,有的直接服务报表、看板和业务决策。

一、一句话总览

如果只用一句话解释这些概念,可以这样理解:

STG:在进入正式数仓前先接住和缓冲数据
ODS:把原始数据接进来,尽量保留现场
TMP:承接加工过程里的临时中间结果
DWD:把数据清洗成可信、可复用的明细事实
DIM:沉淀业务对象的维度信息
DWS:围绕主题和实体做公共汇总
ADS:面向具体应用、报表、看板和接口输出结果

它们串起来大概是这样:

业务系统 / 日志 / MQ / 第三方数据
          ↓
        STG / ODS
          ↓
      TMP(可选)
          ↓
        DWD + DIM
          ↓
          DWS
          ↓
          ADS
          ↓
    BI 看板 / 经营分析 / 风控策略 / 推荐特征 / API 服务

注意,数仓分层不是为了显得架构很复杂,而是为了让数据在流转过程中逐步变得可信、可复用、可追踪、可服务

二、为什么需要数仓分层

假设公司只有一张订单业务库表,分析师想看城市维度 GMV,运营想看商家转化,风控想看异常下单,算法想做用户购买特征。如果所有人都直接查业务库或原始日志,很快会出现几个问题。

  • 口径不一致:有人把退款订单算进 GMV,有人不算;有人按支付时间统计,有人按下单时间统计。
  • 逻辑重复:每个报表都重复写订单状态过滤、时间分区、用户城市映射、商家类目 Join。
  • 链路不可追踪:报表数字异常时,不知道问题出在采集、清洗、维表、汇总还是展示层。
  • 性能不可控:临时分析和线上报表都扫明细大表,任务互相影响。
  • 治理困难:字段含义、生命周期、负责人、数据质量和权限都散落在各处。

分层的价值就是把这些问题拆开:原始数据归原始层,公共清洗归明细层,通用汇总归汇总层,具体应用归应用层。这样一来,数据问题可以定位,指标口径可以复用,报表建设也不会每次从零开始。

三、ODS:原始数据层

ODS 通常叫 Operational Data Store,国内数仓里经常翻译成操作数据层、原始数据层、贴源层

它的核心职责是:把业务系统、日志系统、消息队列、第三方接口里的数据接入数仓,并尽量保留原始形态

比如订单业务库里有一张表:

mysql.order_info
- order_id
- user_id
- shop_id
- status
- pay_amount
- created_at
- updated_at

同步到数仓 ODS 后,可能会变成:

ods_trade_order_info_di
- order_id
- user_id
- shop_id
- status
- pay_amount
- created_at
- updated_at
- dt

这里的 di 常表示按天增量分区,dt 是分区字段。不同公司命名不完全一样,但语义类似:这张表接近源系统,主要用于保留当天同步来的原始订单数据。

ODS 层通常有几个特点:

  • 字段尽量贴近源系统,不轻易改业务含义;
  • 保留原始状态值、原始时间、原始主键;
  • 可以做必要的格式修正、去重、分区补齐,但不做重业务口径加工;
  • 主要服务下游加工和问题追溯,不直接服务复杂分析。

一句话:ODS 是数据进入数仓后的第一现场

四、STG:临时接入和缓冲层

STG 通常是 Staging 的缩写,也叫暂存层、缓冲层。不是每个公司都会把 STG 单独作为正式分层,有些公司会把它并入 ODS。

STG 更偏技术缓冲,常见用途包括:

  • 接收还没有完全校验的原始文件;
  • 承接临时导入、历史回灌、一次性修复数据;
  • 做格式转换前的落盘,例如 JSON、CSV、日志行暂存;
  • 隔离外部系统波动,避免直接污染正式 ODS 表。

可以把 STG 理解成进入正式数仓前的卸货区。货先放在这里,检查包装、数量和格式,再进入 ODS 或 DWD。

五、TMP:临时加工层

TMP 通常是 Temporary 的缩写,也叫临时层、临时表层、临时加工层。它和 STG 都带一点「临时」的味道,但位置和职责不一样:STG 更像进入正式数仓前的接入缓冲,TMP 更像加工任务内部的中间草稿

TMP 的核心职责是:把复杂加工过程中的中间结果临时落下来,方便拆分 SQL、复用本次任务结果、排查问题或承接一次性数据处理

比如订单明细加工可能需要先把订单、支付、退款、优惠、配送等多张表拼到一起。如果一条 SQL 太长,或者中间结果需要反复校验,就可能先落一张临时表:

tmp_trade_order_pay_refund_di
- order_id
- user_id
- shop_id
- pay_amount
- refund_amount
- coupon_amount
- delivery_status
- dt

这张表的价值不是对外提供稳定口径,而是帮助当前加工任务把复杂逻辑拆开。任务跑完、回灌结束或问题排查完成后,它就应该被清理,或者至少有明确的保留周期。

TMP 层常见用途包括:

  • 把很长的 SQL 拆成几步,降低开发和排障难度;
  • 承接历史回灌、数据修复、一次性分析的中间结果;
  • 在 DWD、DWS、ADS 加工过程中临时缓存 Join 或聚合结果;
  • 保存校验样本、异常数据、重跑任务的阶段性结果。

TMP 最大的风险是「临时表长期化」。如果一张 TMP 表开始被多个下游长期依赖,它就不应该继续叫 TMP,而应该被重新建模,沉淀到 DWD、DWS 或 ADS。

一句话:TMP 是加工过程里的临时工作台,不是稳定的数据资产层

六、DWD:明细事实层

DWD 通常叫 Data Warehouse Detail,也就是明细数据层。它是数仓里非常关键的一层。

DWD 的核心职责是:把 ODS 中贴源但不够好用的数据,清洗成业务含义清晰、粒度稳定、可复用的明细事实表

比如 ODS 里订单状态可能很混乱:

status = 0  创建
status = 1  已支付
status = 2  已取消
status = 3  已退款
status = 9  测试订单

DWD 层会做标准化处理,例如:

  • 过滤测试订单、脏数据、重复数据;
  • 统一字段命名和字段类型;
  • 补充业务可理解的状态枚举;
  • 把订单、支付、退款、履约等事实拆成稳定主题;
  • 保证一张事实表有明确粒度,比如一行代表一笔订单、一笔支付、一次曝光、一次点击。

一张 DWD 订单明细表可能是:

dwd_trade_order_detail_di
- order_id
- user_id
- shop_id
- city_id
- category_id
- order_status
- is_paid
- is_refund
- pay_amount
- order_time
- pay_time
- dt

这张表仍然是明细表,不是汇总表。它的重点不是提前算好报表指标,而是让下游可以基于可信明细继续加工。

一句话:DWD 是把原始数据变成标准业务事实

七、DIM:维度层

DIM 是 Dimension 的缩写,也就是维度层

事实表记录「发生了什么」,维度表解释「是谁、在哪、属于什么、当前是什么状态」。例如:

  • 订单事实:一笔订单支付了 89 元;
  • 用户维度:这个用户来自哪个城市、注册渠道是什么、会员等级是什么;
  • 商家维度:这个商家属于哪个品类、所在商圈是什么、是否品牌商家;
  • 商品维度:这个商品属于哪个类目、价格带是什么、是否新品。

常见维表命名类似:

dim_user_df
  用户维度,全量快照表

dim_shop_df
  商家维度,全量快照表

dim_item_category_df
  商品类目维度,全量快照表

这里的 df 常表示按天全量快照。维度数据容易变化,例如用户等级会变、商家品类会调、城市归属会改,所以维表经常需要保留每日快照,甚至用拉链表记录历史版本。

DIM 层最容易被低估。没有好的维度层,DWD 明细表就很难被稳定解释,DWS 和 ADS 的口径也会漂。

八、DWS:公共汇总层

DWS 通常叫 Data Warehouse Summary,也就是汇总数据层、公共汇总层、主题汇总层

DWS 的核心职责是:围绕稳定的业务主题、实体和时间周期,把 DWD 明细事实聚合成可复用的公共汇总数据

比如很多下游都会用到这些数据:

  • 用户近 1 天、7 天、30 天下单次数;
  • 商家每天的订单数、支付金额、退款金额;
  • 城市每天的访问人数、下单人数、支付转化率;
  • 商品类目每天的曝光、点击、成交、GMV。

如果这些逻辑每个报表都单独算,就会重复、慢、不一致。DWS 会把它们沉淀成公共层。

dws_trade_user_1d_di
- user_id
- order_cnt_1d
- paid_order_cnt_1d
- pay_amount_1d
- refund_amount_1d
- dt

dws_trade_shop_1d_di
- shop_id
- order_cnt_1d
- pay_amount_1d
- refund_order_cnt_1d
- dt

DWS 的关键词是公共。它不应该只服务一个页面、一个临时分析或一个很窄的需求,而应该沉淀多个场景都能复用的主题汇总能力。

一句话:DWS 是把明细事实聚合成公共业务能力

九、ADS:应用数据层

ADS 通常叫 Application Data Service,也叫应用层、数据服务层、应用数据层

ADS 的核心职责是:面向具体应用场景组织最终数据形态

比如经营看板需要一个城市经营日报,它关心的不是下游能否复用,而是页面需要什么字段、什么排序、什么口径、什么更新时间。

ads_city_business_dashboard_di
- city_id
- city_name
- gmv
- order_cnt
- pay_user_cnt
- new_user_cnt
- refund_rate
- conversion_rate
- target_finish_rate
- dt

ADS 层常见服务对象包括:

  • BI 报表和可视化大屏;
  • 经营分析日报、周报、月报;
  • 业务系统里的数据接口;
  • 风控策略、营销圈选、用户画像;
  • 算法特征、实验分析和效果归因。

ADS 的特点是强场景、强交付、强时效。它可以依赖 DWS,也可以在必要时回到 DWD 取更细粒度数据,但不应该把大量重复清洗逻辑长期堆在 ADS 里。

一句话:ADS 是把数仓能力包装成业务能直接使用的数据产品

十、用订单分析串起来看一遍

假设我们要做一个「城市经营看板」,展示每天每个城市的 GMV、订单数、支付用户数和退款率。

一条比较典型的数据链路是:

1. ODS
   ods_trade_order_info_di
   从订单库同步原始订单数据

2. TMP(可选)
   tmp_trade_order_pay_refund_di
   在复杂加工中临时拼接订单、支付、退款等中间结果

3. DWD
   dwd_trade_order_detail_di
   清洗测试订单、统一状态、明确一行一单

4. DIM
   dim_city_df / dim_shop_df / dim_user_df
   补充城市、商家、用户等解释信息

5. DWS
   dws_trade_city_1d_di
   按城市和日期汇总订单数、GMV、退款金额

6. ADS
   ads_city_business_dashboard_di
   拼出看板所需字段,补充目标达成率、同比环比、排序展示

同一个指标在不同层的形态不同。DWD 里是一笔笔订单,DWS 里是按城市和天聚合后的公共结果,ADS 里是经营看板直接使用的最终宽表。

十一、几层之间到底怎么区分

层级核心问题典型粒度主要使用者
STG数据先接住文件、批次、原始记录数据开发、同步任务
ODS原始数据保留下来接近源表或原始日志数据开发、排障、下游加工
TMP中间结果先落下任务中间表、临时宽表、批次结果数据开发、一次性分析
DWD明细事实变可信订单、支付、点击、曝光等事实明细数据开发、分析师、算法工程
DIM业务对象可解释用户、商家、商品、城市等维度所有下游
DWS公共指标可复用按主题、实体、时间窗口汇总数据开发、分析师、应用层
ADS业务场景能直接用报表、接口、策略、特征所需形态业务方、BI、产品、算法、服务接口

最关键的区分方法不是看表名,而是看它的责任边界

  • 如果主要为了保留源数据,是 ODS;
  • 如果主要为了支撑某个加工任务的中间计算,是 TMP;
  • 如果主要为了沉淀可信明细,是 DWD;
  • 如果主要为了解释实体属性,是 DIM;
  • 如果主要为了复用公共汇总,是 DWS;
  • 如果主要为了服务具体应用,是 ADS。

十二、常见表命名怎么读

很多数仓表名会长成这样:

dwd_trade_order_detail_di
│   │     │     │      └─ 分区/更新方式:di 表示日增量
│   │     │     └──────── 粒度或形态:detail 明细
│   │     └────────────── 业务对象:order 订单
│   └──────────────────── 业务域:trade 交易
└──────────────────────── 层级:dwd 明细层

常见后缀大致可以这样理解,不同团队会有差异:

后缀常见含义例子
didaily incremental,按天增量当天新增或变更订单
dfdaily full,按天全量快照每日用户维度快照
1d最近 1 天或按天统计用户日订单数
7d最近 7 天窗口统计用户近 7 天点击数
td截至当天累计本月至今累计 GMV

命名不是越长越好,而是要让别人一眼看出:这张表在哪一层、属于哪个业务域、是什么对象、什么粒度、怎么分区。

十三、事实表和维度表

理解 DWD 和 DIM 时,经常会遇到两个词:事实表和维度表。

事实表记录业务事件,例如订单、支付、退款、曝光、点击、发货、签收。事实表里通常有可度量的指标字段,例如金额、数量、时长、次数,也有连接维度的 key,例如 user_idshop_idcity_id

维度表记录业务对象的属性,例如用户性别、城市名称、商家品类、商品品牌。维度表让事实可以被分析和解释。

事实:用户 1001 在 2026-06-16 支付了一笔 89 元订单
维度:用户 1001 是北京用户,商家属于快餐品类,城市属于华北大区

数仓里常说的「星型模型」就是一张事实表周围连接多张维度表。DWD 里沉淀可信事实,DIM 里沉淀稳定维度,DWS 再基于它们做公共汇总。

十四、DM:数据集市是什么

DM 通常指 Data Mart,也就是数据集市。它不是所有公司都会单独放在标准分层里,但在大型组织里很常见。

可以把数据集市理解成面向某个业务线、部门或主题域的小型数仓。例如:

  • 营销数据集市;
  • 风控数据集市;
  • 用户增长数据集市;
  • 履约配送数据集市。

它可能包含自己的 DWS 和 ADS,也可能只是从集团公共数仓中抽取一部分数据,做面向部门的二次组织。

如果公共数仓解决的是全公司统一口径,数据集市解决的就是某个业务方向的高频使用和快速交付。

十五、实时数仓里的这些层还存在吗

实时数仓里仍然会使用类似分层,只是计算引擎和数据形态变了。

Kafka 原始日志       → realtime_ods
Flink 清洗明细       → realtime_dwd
Flink 窗口聚合       → realtime_dws
Doris / ClickHouse   → realtime_ads

实时链路更强调低延迟、乱序处理、状态管理、Exactly-once、维表 Join 和结果更新。它不像离线数仓那样天然按天批处理,所以层级边界有时会更薄。

但原则仍然相同:原始数据、可信明细、公共汇总、应用服务要尽量分清。否则实时链路也会陷入同样的问题:口径重复、逻辑散落、异常难排查。

十六、常见误区

数仓分层最常见的问题,不是大家不知道 ODS、DWD、DWS、ADS 这几个词,而是表名像分层,实际职责没有分清。

误区问题更好的做法
ODS 里做太多清洗原始现场丢失,排障困难ODS 尽量贴源,清洗放到 DWD
TMP 长期被下游依赖临时表变成隐形公共层,血缘和口径失控设置生命周期,复用稳定后沉淀到 DWD/DWS/ADS
DWD 做成汇总表明细不可复用,下游难扩展DWD 保持稳定明细粒度
DWS 只服务一个报表公共层退化成应用层DWS 围绕主题沉淀可复用汇总
ADS 堆满复杂清洗逻辑口径散落,维护成本高把公共逻辑下沉到 DWD/DWS
维表没有历史版本回看历史数据时口径漂移用快照表或拉链表管理变化
只分层不治理表很多但没人敢用补齐负责人、血缘、质量、SLA、生命周期

十七、真正落地时怎么做

如果从零建设一个数仓,不应该先纠结表名前缀,而应该先回答几个问题。

  • 业务域怎么划分:交易、用户、商家、商品、履约、营销、风控分别是什么边界?
  • 核心事实有哪些:订单、支付、退款、曝光、点击、配送、核销分别是什么粒度?
  • 核心维度有哪些:用户、商家、商品、城市、类目、渠道、活动如何管理?
  • 公共指标有哪些:GMV、订单数、支付用户数、转化率、退款率的统一口径是什么?
  • 服务场景有哪些:报表、API、策略、算法、实验分析分别需要什么数据形态?
  • 治理机制是什么:谁负责、什么时候产出、质量怎么校验、失败怎么报警、历史保留多久?

分层只是骨架,真正让数仓变好用的是指标口径、数据质量、任务稳定性、元数据、血缘、权限和文档。

结语

ODS、DWD、DWS、ADS 这些缩写本身并不神秘。它们只是把数据从「原始」到「可用」再到「可服务」的过程拆成几个责任清晰的阶段。

STG 负责接入缓冲,ODS 保留现场,TMP 承接加工过程里的临时中间结果,DWD 沉淀可信明细,DIM 解释业务对象,DWS 提供公共汇总,ADS 面向具体应用交付结果。DM 等层则根据团队规模、数据复杂度和交付方式补充使用。

判断一张表应该放在哪一层,不要只看它叫什么,而要问:它是在接入缓冲、保留原始数据、支撑临时加工、清洗明细事实、解释维度属性、沉淀公共汇总,还是服务具体应用?

这就是数仓分层最重要的意义:让数据链路里的每一步都有明确责任,让同一份数据可以被更多人放心复用。