Kimball 维度建模 vs Inmon 范式建模 渠道数仓深度研究

Kimball 维度建模(星型模型:事实表+维度表,分析场景出发、快、好懂,互联网主流)对比 Inmon 范式建模(3NF,自顶向下、企业级一致性、开发重);总监口径"渠道数据天然适合维度建模——经销商/终端/产品/时间是维度,进销存流水是事实"是否成立、怎么落地。

目录
  1. 0. 一页结论
  2. 1. 历史脉络:一对"冤家",两种世界观
  3. 2. 两个体系到底在主张什么
  4. 2.1 Inmon:自顶向下的 CIF
  5. 2.2 Kimball:自底向上的总线架构
  6. 2.3 正面对照表
  7. 3. Kimball 方法论全拆解(实操主角)
  8. 3.1 四步法(顺序不可换)
  9. 3.2 事实表的四种类型
  10. 3.3 维度设计模式速查
  11. 3.4 一致性维度与总线矩阵
  12. 3.5 聚集与衍生
  13. 4. Inmon 体系补充拆解
  14. 5. 第三条路与 2026 现状
  15. 5.1 Data Vault 2.0:Inmon 的敏捷替身
  16. 5.2 国内互联网分层与宽表现实
  17. 5.3 语义层与 LLM:维度建模的二次升温
  18. 5.4 湖仓时代的分层映射
  19. 6. 渠道(经销商)数据建模实操
  20. 6.1 为什么"天然适合"维度建模——逐条论证
  21. 6.2 核心事实表草案(示意 DDL)
  22. 6.3 关键维度处理要点
  23. 6.4 渠道特有陷阱清单
  24. 6.5 渠道数仓里"必须像 Inmon"的部分
文本版 · 供搜索与朗读

Kimball 维度建模 vs Inmon 范式建模 深度研究

数据仓库 · 建模方法论 · 深度研究

Kimball 维度建模 vs Inmon 范式建模
渠道数仓深度研究

Kimball 维度建模(星型模型:事实表+维度表,分析场景出发、快、好懂,互联网主流)对比 Inmon 范式建模(3NF,自顶向下、企业级一致性、开发重);总监口径"渠道数据天然适合维度建模——经销商/终端/产品/时间是维度,进销存流水是事实"是否成立、怎么落地。

目 录

一页结论
历史脉络:一对"冤家",两种世界观
两个体系到底在主张什么
Kimball 方法论全拆解(实操主角)
Inmon 体系补充拆解
第三条路与 2026 现状
渠道(经销商)数据建模实操
总监口径逐句评审
选型决策指南
参考资料

0. 一页结论

这不是对错之争,是两种一致性交付路线之争。 Inmon 认为"企业级一致"必须先建集中式 3NF 原子库再长出集市(自顶向下);Kimball 认为"企业级一致"可以由共享的一致性维度在逐个建设的集市之间背书(自底向上)。前者一致性有架构强制力但交付慢,后者交付快但一致性靠纪律。

"Inmon = 严格 3NF"是二手文献的简化。 Inmon 的仓库四特征是面向主题、集成、时变、非易失,3NF 只是他"一处事实、一处存储"原则的常规实现手段。实践中这么记没有大错,但面试/评审场合知道这个辨析是加分项。

2026 年的现实是混合架构,不是单选。 主流形态:贴源层(ODS / Data Vault)→ 整合层 → Kimball 星型集市/宽表(金层)。BARC 2023–2025 调研里一类企业(best-in-class)Data Vault 采用率 34% vs 滞后者 15%,且常见搭配正是 "DV 做银层、Kimball 做金层"。Inmon 的企业级诉求没有死,它被一致性维度、OneID、语义层接管了。

维度建模在 LLM 时代反而更吃重。 2024–2026 语义层复兴(dbt Semantic Layer、Cube、AtScale、Looker)本质是总线矩阵的产品化;ChatBI/text-to-SQL 对模型形状高度敏感——星型 > 宽表 > 3NF,因为星型把 join 路径、粒度、业务口径都显式化了,正好是 LLM 需要的"契约"。

总监口径成立,且可执行。 渠道业务分析驱动、源系统异构(DMS/ERP/POS/TMS/费控)、维度天然共享、事实天然是流水——四个特征全部指向维度建模。但有三处要补:进/销/存不是同一种事实表(存=周期快照=半可加);返利/费用结算子域需要财务级强一致口径,别混进分析事实;一致性维度(总线矩阵)才是这套说法的隐形承重墙,必须先行。

1. 历史脉络:一对"冤家",两种世界观

年份
事件

1991
Bill Inmon《Building the Data Warehouse》出版,"数据仓库之父"头衔确立;给出仓库四特征定义(面向主题、集成、时变、非易失),提出 CIF(Corporate Information Factory)架构

1996
Ralph Kimball《The Data Warehouse Toolkit》出版(第 3 版 2013),维度建模方法论定型

1997–2005
两人公开论战:Inmon 阵营批 Kimball"集市孤岛、无企业视图";Kimball 阵营批 Inmon"3NF 用户看不懂、交付以年计"。Kimball 写过 "Is There a Corporate Information Factory?" 直接点名回应

2008
Inmon 出《DW 2.0: The Architecture for the Next Generation of Data Warehousing》,补元数据、非结构化数据、活跃归档层——也是对批评的回应

2000s–2010s
Dan Linstedt 提出 Data Vault(2.0 标准 2013 前后定型,hash key + 自动化),定位为"Inmon 的敏捷替身"

2015
Kimball Group(Kimball & Margy Ross)退休,kimballgroup.com 设计技巧库由 Decisionworks 维护至今

2017
阿里《大数据之路》出版,OneData 体系(OneModel/OneID/OneService)把 Kimball 四步法内化进 ODS-DWD-DWS-ADS 分层,成为中国互联网事实标准

2020s
湖仓一体(Iceberg/Delta/Paimon)+ medallion 分层;dbt 把 Kimball 带进新一代数据栈;语义层复兴,维度建模二次升温

一个常被忽略的事实:两人晚年实际上趋同了。Inmon 承认下游消费需要维度化集市(CIF 里本来就有 DM 层);Kimball 承认企业级一致性必须有人管(他的答案就是一致性维度 + 总线矩阵,这本身就是一种企业级契约)。争论的本质从"谁对"变成"先建哪一层、一致性由谁背书"。

2. 两个体系到底在主张什么

2.1 Inmon:自顶向下的 CIF

flowchart LR
SRC[源系统] --> ODS[ODS 贴源]
ODS --> EDW[EDW 原子库<br>规范化建模 3NF]
EDW --> DM1[部门集市 A<br>维度化]
EDW --> DM2[部门集市 B<br>维度化]
EDW --> OLAP[OLAP / 报表 / 探索]

核心主张:数据仓库是企业级资产,必须先建"一处事实、一处存储"的原子库(粒度最细、全集成、带历史、不再更新),各部门集市从原子库派生。

3NF 的真实角色:消除冗余、保证同一事实只有一份(改一处全局生效),支撑 BI 之外的复用(如反哺业务系统、监管报送)。Inmon 本人的定义四特征里没有 3NF;说"规范化建模"比"3NF"更准。

代价:企业级 ER 建模是稀缺技能,第一个可用集市往往要 6–18 个月;需求方在拿到任何东西之前要先陪跑建模。这就是"开发重"三个字的实指。

收益:一致性有架构强制力,口径纠纷在建模阶段就被裁决;后续换集市、加集市成本递减。

2.2 Kimball:自底向上的总线架构

flowchart TB
SRC1[DMS] --> P1[集市 进货]
SRC2[POS] --> P2[集市 动销]
SRC3[费控] --> P3[集市 费用]
DIM[一致性维度<br>商品 经销商 终端 日期 组织 促销] -.-> P1
DIM -.-> P2
DIM -.-> P3
P1 --> APP[BI / 分析]
P2 --> APP
P3 --> APP

核心主张:直接从业务过程(business process,不是部门、不是报表)出发逐个建星型集市;用一致性维度(conformed dimensions,跨集市同一维度、同一属性、同一口径)作为"总线"把集市拼成企业整体。

关键洞察:企业级一致性不必然来自集中式架构,可以来自共享的维度标准。总线矩阵(行=业务过程,列=一致性维度)既是设计工具也是组织契约。

收益:每个业务过程独立交付,几周出第一个星型;表形状就是业务问题形状("某经销商某月某品类的销量"直接就是事实表的一行聚合),业务人员看得懂、BI 工具优化得好。

代价:一致性靠纪律而非架构——谁先建维度谁定标准,晚来的集市有动机私建维度(维度蔓延);跨集市复杂关联分析(不是沿维度下钻,而是真·多对多企业级查询)不是它的主场。

2.3 正面对照表

维度
Kimball 维度建模
Inmon 范式建模(CIF)

建模出发点
业务过程、分析场景
企业主题域、企业 ER

设计顺序
自底向上,逐集市交付
自顶向下,先原子库后集市

核心构件
事实表 + 维度表(星型)
3NF 实体关系模型

一致性机制
一致性维度 + 总线矩阵(纪律背书)
集中原子库(架构背书)

首个可用交付
周–月级
6–18 个月

查询友好度
极高(join 模式固定、少而宽)
低(多表规范化 join)

抗源系统变化
中(维度 SCD 吸收属性变化)
高(EDW 层隔离)

面向人群
业务分析师、BI
数据建模师、平台工程师

典型失败模式
维度蔓延、口径漂移、粒度混乱
交付周期拖死项目、建模过度

主阵地
互联网、零售、快消、SaaS
金融核心、电信、强监管行业

3. Kimball 方法论全拆解(实操主角)

3.1 四步法(顺序不可换)

选业务过程:建模对象是"进货""出库""动销"这种组织内发生的原子事件流,不是"销售部""市场部"。一个业务过程一个(组)事实表。判据:有没有可数的、带时间戳的事件发生。

声明粒度:这张事实表一行代表什么。全流程最重要的一步——"经销商销售出库表,一行 = 一张出库单上的一个商品行项(最小销售单元)"。粒度声明之后,后面所有维度和度量都被锁死;粒度说不清的表一定会在半年后口径爆炸。原子粒度优先:聚合可以随时重算,细节丢了就没了。

定维度:回答"谁、什么、何时、何地、为何、如何"。粒度声明里每个名词就是一个维度。

定事实:必须是粒度声明允许的度量。分三类可加性——可加(销量、金额,任意维度求和)、半可加(库存、余额,可跨实体求和、不可跨时间求和)、不可加(比率、单价,只能存分子分母)。

3.2 事实表的四种类型

类型
一行是什么
渠道场景例子
要点

事务事实
一个时间点的一次事件
进货单行项、销售出库行项、POS 小票行项
最常见、最细、可加

周期快照
一个周期结束时的状态
经销商日末库存、月末客户余额
半可加事实,跨周期求和非法

累积快照
一个流程的整条生命周期
订单从下单→发货→签收→开票→结算的各里程碑日期与数量
有不确定的结束点也要建,里程碑列可空

无事实事实
一次关系/覆盖的记录
经销商×活动覆盖关系、终端陈列登记
没有度量,只有外键组合,本身可数(count)

3.3 维度设计模式速查

退化维(degenerate dimension):单据号没有对应维度表,直接留在事实表做分组/追溯键。

垃圾维 / 杂项维(junk dimension):一堆低基数标志位(是否首单、是否赠品、支付方式)打成一张小维度的单键,避免事实表列爆炸。

角色扮演(role-playing):同一日期维度在事实表里出现多次(下单日期/发货日期/入账日期),物理上一张维表多个视图/别名。

支架维(outrigger):维度套维度(商品维挂品牌维),谨慎用,最多一层,避免雪花化回潮。

桥接表(bridge):事实表外键指向的多对多关系(经销商×代理品牌),可带权重因子(weighting factor)分配事实。

迷你维(mini-dimension):高频变化的数值型/标志型属性(经销商信用分档)单独拆表,与主维度通过事实表共同引用——避免 SCD2 把大维度撑爆。

SCD(缓慢变化维):Type 0 原值不动(日期维的属性)、Type 1 直接覆盖(纠错)、Type 2 加新行保留历史(主力,经销商等级/区域/状态变更、商品层级调整)、Type 3 加列存上一值(极少用)、Type 4/5/6/7 是迷你维与 1/2/3 的组合套路(6 = 1+2+3 混合,"当前属性照进历史行")。实操纪律:业务含义变化用 Type 2,纯纠错用 Type 1,两者混用是口径事故的头号来源。

迟到数据(late-arriving):迟到的事实要回填正确的历史维度键(需要维度表保留"代理键查找"能力);迟到的维度属性先挂"Unknown"行再回补。

3.4 一致性维度与总线矩阵

一致性维度三同级:相同(shared,两集市直接共用一张物理表)、相同但截取(如集团商品维 vs 某事业部行)、子集/上卷(渠道维 vs 渠道大类维)。只要落在这三级之内,集市间就可以安全地钻透、组合。

总线矩阵是整个体系的治理中枢:

业务过程 \ 维度
日期
商品
经销商
终端
组织
促销活动

经销商进货(采购入库)
●
●
●

●

销售出库(卖进终端/二批)
●
●
●
●
●
●

终端动销(POS)
●
●

●

●

库存(周期快照)
●
●
●

费用与返利
●
●
●

●
●

订单履约(累积快照)
●
●
●
●
●

矩阵先于任何一张表存在:先在业务上吵清楚每一列维度的口径(什么叫"有效终端"、商品层级怎么归属),再开工建表。这一步就是 Kimball 版的"企业级建模",也是总监口径里最容易被漏掉的部分。

3.5 聚集与衍生

原子事实表之上建聚集表(aggregate,如"经销商×月×品类")加速常见查询;同一聚集族内所有表必须由同一套事实派生("聚集导航"一致性)。现代引擎(StarRocks/Doris/ClickHouse 物化视图、摘要表)让聚集的工程成本大幅下降,但"原子层优先"的纪律没变。

4. Inmon 体系补充拆解

CIF 分层职责:ODS(近实时贴源)→ EDW(原子、集成、历史、只读)→ DM(部门集市,通常反而是维度化的)→ 前台(报表/OLAP/探索仓)。注意:Inmon 体系的集市层最终也是星型——两家争的不是维度建模有没有用,而是它该出现在哪一层。

DW 2.0 的修补:加入元数据主线、非结构化内容、活跃归档层、迭代式开发的部分承认。实际落地远少于 CIF。

什么时候 3NF EDW 划算:金融核心账务、监管报送(口径多年稳定、可解释性要求极高)、需要把仓库数据反哺业务系统的场景、源系统极多且必须企业级消歧的大集团主干。

"开发重"的另一面:原子库建好之后,加集市是轻活(建模已完成,集市只是投影)。Inmon 是前期重、边际轻;Kimball 是前期轻、治理随规模累进。两者交付成本曲线是交叉的,交叉点大致在"主题域基本覆盖企业核心流程"的地方。

5. 第三条路与 2026 现状

5.1 Data Vault 2.0:Inmon 的敏捷替身

结构:Hub(业务键,稳定不变)+ Link(业务键之间的关系/事件)+ Satellite(描述属性+全历史,一颗卫星对应一个源系统,新接一个源就挂一颗新卫星,Hub/Link 永不改动)。2.0 加 hash key(并行加载友好)、业务 Vault 层(可重算的计算规则)。

主张:贴源入仓"按接收到的原样存"+ 技术字段(加载时间、记录源、hash),全部业务规则推迟到下游——把 Inmon 的可集成、可审计拿过来,去掉 3NF 前期建模的脆和重。

现实定位:Hub/Link/Satellite 做银层(整合+审计+历史),Kimball 星型做金层(消费)是当前最成熟的组合拳;纯 DV 直接对外服务的很少(业务用户同样看不懂 Hub/Link/Satellite,正如看不懂 3NF)。

采用率:BARC 调研,一流企业 34% vs 滞后者 15%;自动化工具(Coalesce、Datavault Builder)近乎必需——手写 DV 冗长到不可维护。

5.2 国内互联网分层与宽表现实

阿里 OneData(《大数据之路》):OneModel(统一建模)、OneID(统一主体识别,解决"同一个人/同一个商品多处编码")、OneService(统一服务)。物理分层 ODS→DWD→DWS→ADS;DWD 层就是 Kimball 四步法 + 一致性维度,DWS 是轻度聚合,ADS 是面向场景的宽表。美团、字节、滴滴同构。

大宽表是 Kimball 在现代引擎上的退化形态:把星型 join 预先拍平成一张超宽表喂给 ClickHouse/Doris/StarRocks。收益是查询简单粗暴,代价是口径冗余、ETL 链路翻倍、维度一改全链路重建。当前共识:明细层坚持维度建模(管口径),汇总/加速层允许宽表(管性能)——用分层治理换范式纯度,而不是把宽表当模型。

5.3 语义层与 LLM:维度建模的二次升温

总线矩阵里"行=业务过程、列=一致性维度"的表格,本质就是语义层的静态版。2024–2026 dbt Semantic Layer、Cube、AtScale、Looker LookML 的复兴,是给一致性维度补上了机器可执行的形态。

ChatBI / text-to-SQL / 数据 Agent 对模型形状极其敏感:3NF 上 LLM 要推理十几张表的主外键和业务含义,幻觉率陡增;星型把 join 路径压成"事实表居中、维度环绕"的固定模式,粒度和口径都以列名/维表属性显式存在——星型模型是 LLM 天然能读懂的数据契约。这是 2025–2026 各家把"先维度建模、再上 AI 问数"当标准前置动作的根本原因。

5.4 湖仓时代的分层映射

flowchart LR
A[bronze 贴源] --> B[silver 整合<br>ODS标准化 / DV / 3NF]
B --> C[gold 消费<br>Kimball 星型 / 宽表]
C --> D[BI 看板]
C --> E[语义层]
E --> F[ChatBI / Agent]

三代人在一张图里合流了:bronze≈ODS,silver≈Inmon/DV 的地盘,gold≈Kimball 的地盘。历史问题没有标准答案的地方,工程演化给出的答案是分层共存。

6. 渠道(经销商)数据建模实操

6.1 为什么"天然适合"维度建模——逐条论证

需求形状是分析的:渠道盘库、价盘管理、库存健康度(周转/库龄)、终端动销、费效比、经销商分级——全部是"沿维度看度量"的问题,这正是星型的定义域。

源系统异构且脏:DMS(经销商管理)、ERP(财务/进销存)、POS(终端动销)、TMS(物流)、费控系统(费用返利)——不存在一家供应商能预先罩住全部的企业 ER;维度建模允许按业务过程逐个吃、逐个交付,Inmon 式先全企业建模在这里会直接卡死。

维度天然共享且稳定:经销商、终端、商品、日期、组织、促销活动——每个业务过程都在用同一批"谁/什么/何时/何地",一致性维度一次投入全局复用。

事实天然是流水:进销存是标准事件流,事务事实表拿来就用。

6.2 核心事实表草案(示意 DDL)

-- 事务事实:经销商进货(采购入库)
fact_procurement_receipt (
date_key, dealer_key, product_key, org_key,
supplier_key, purchase_type_key,
receipt_no, -- 退化维
order_qty, receipt_qty, amount_excl_tax, amount_incl_tax
)
-- 粒度:一张入库单的一个商品行项(最小销售单元)

-- 事务事实:销售出库(卖进终端/二批)
fact_channel_sales (
order_date_key, ship_date_key, dealer_key, store_key,
product_key, org_key, promo_key, sales_type_key,
order_no, line_no, -- 退化维
order_qty, ship_qty, gross_amount, discount_amount, net_amount
)
-- 粒度:一张出库单的一个商品行项;order_date/ship_date = 角色扮演日期维

-- 周期快照:经销商库存(半可加!)
fact_inventory_snapshot (
snapshot_date_key, dealer_key, warehouse_key, product_key,
on_hand_qty, allocated_qty, in_transit_qty, amount_at_cost
)
-- 粒度:经销商×仓×商品×日;on_hand 可跨经销商/商品求和,跨日期求和非法

-- 累积快照:订单履约
fact_order_fulfillment (
order_line_key, -- 稳定唯一键
order_date_key, promised_date_key, ship_date_key,
delivered_date_key, invoice_date_key, settlement_date_key,
order_qty, shipped_qty, settled_amount, current_status_key
)

-- 费用/返利 accrual(粒度必须单独声明!)
fact_rebate_accrual (
period_key, dealer_key, product_key, promo_key, fee_type_key,
accrual_amount, settled_amount
)

6.3 关键维度处理要点

维度
处理

dim_dealer 经销商
SCD2(等级/区域/合作状态变更);主数据多头(ERP 编码/电商店铺 ID/费控 ID)在维度加载层做 OneID 归一,桥接表存映射

dim_store 终端
SCD2(业态/归属经销商变更);"有效终端"判定做成维度属性而不是事实过滤

dim_product 商品
SCD2 + 层级重归类:品类树调整时新开行(Type 2)而不是刷历史;条码/规格/单位换算(箱↔最小单元)放维度,事实表只存最小单元

dim_date 日期
Type 0,含农历/节气/大促标记(618/双11/春节档);角色扮演多个别名

dim_org 组织
大区/省区/办事处层级上卷路径;组织调整走 SCD2

dim_promo 活动
活动维独立(有层级、有日期范围);POS 流水里一堆小标志(是否赠品/是否会员价)打 junk dimension

bridge_dealer_brand
经销商×代理品牌多对多,带权重因子分配跨品牌事实

6.4 渠道特有陷阱清单

库存半可加:BI/报表引擎必须拦截"跨日期求和库存",正确口径是"期末值/区间均值/期初+期末均值"。这是渠道数仓最高频的低级错误。

返利的两种粒度:结算事实(法律意义,跟财务总账勾稽,粒度=结算单行)与归因事实(分析意义,摊到活动/商品/月的估算)必须分表。混在一张表里,财务对不上账,分析算不精费效。

三层漏斗口径:卖进(经销商进货)≠ 卖出(经销商出库)≠ 动销(终端 POS)。渠道库存 = 累计卖进 − 累计卖出,串货分析要靠"出库目的地终端的注册区域 vs 经销商区域"交叉。

冲账与补录:经销商手工单据迟到、红字冲销是常态。事实表要有 reversal 语义(负数行 + 关联原单号),禁止物理删除重传。

含税/不含税、币种、多单位:口径进列名(amount_excl_tax/amount_incl_tax),事实表不存"裸金额"。

维度质量 = 上限:维度建模的命门在主数据。经销商主数据乱,星型建得再漂亮也是垃圾进垃圾出——这块投入(OneID、清洗规则)省不得,它就是渠道版"Inmon 式地基"。

6.5 渠道数仓里"必须像 Inmon"的部分

财务对账子域:应收、返利结算、费用核销与总账的勾稽,用强一致口径建模(甚至直接消费财务侧 3NF 模型),不要让分析口径污染结算口径。

主数据治理:商品主数据、经销商主数据、组织主数据的企业级标准——这部分就是 Inmon 思想在新架构里的存活形态。

7. 总监口径逐句评审

总监原话
评审

"渠道数据天然适合维度建模"
✅ 成立。四个证据见 6.1;补充条件:主数据治理必须先到位,否则维度建模的优势兑现不了

"经销商/终端/产品/时间是维度"
✅ 对,但不全。至少补:组织(大区/省区)、促销活动、渠道类型;而且这些维度能不能用取决于 SCD 策略——经销商等级一变历史就串档,是渠道数仓最常见的事故

"进销存流水是事实"
⚠️ 对但危险。进/销是事务事实,存是周期快照(半可加),履约是累积快照——三种表的粒度、更新方式、合法聚合完全不同。一把梭成一张大宽表是最常见的初级错误

(缺失的一句)
一致性维度先行。总线矩阵先于任何一张事实表存在,这是 Kimball 体系里"企业级一致性"的承重墙,也是总监口径里唯一该补的落漏

8. 选型决策指南

你的处境
选
理由

分析驱动业务、需求变化快、要尽快见效
Kimball
边际交付、业务可读

源系统异构、没有预建模可能
Kimball
按过程逐个吃

强监管、核心账务、口径多年不变
Inmon/3NF
架构强制一致性、可解释

源多且杂、要全量历史+可审计、团队有自动化工具
Data Vault 2.0 银层
按源挂卫星、可重放

上湖仓/现代数据栈
bronze→silver→gold 分层混合
见 5.4,历史两派各占一层

大屏/大并发固定报表
明细层 Kimball + 汇总层宽表/物化视图
分层治理换性能

要上 ChatBI/数据 Agent
Kimball 星型 + 语义层
LLM 友好的数据契约

一句话总纲:小到中规模、分析驱动——直接 Kimball 起步;大型企业多源主干——DV/3NF 整合层 + Kimball 消费层;无论哪条路,一致性维度和主数据治理都是逃不掉的那笔账,Inmon 把它算在前期,Kimball 把它算在纪律里。

9. 参考资料

Kimball, Ross. The Data Warehouse Toolkit, 3rd ed., Wiley, 2013(四步法、SCD、总线矩阵的权威出处)

kimballgroup.com 设计技巧库(Kimball Group 2015 年退休后由 Decisionworks 维护;含 "Reasons to use a 3NF design over a dimensional model" 等对 Inmon 体系的正面回应)

Inmon. Building the Data Warehouse, 4th ed., 2005;DW 2.0, 2008(四特征定义与 CIF)

Linstedt, Ochs. Building a Scalable Data Warehouse with Data Vault 2.0, 2015;Hultgren, Modeling the Agile Data Warehouse with Data Vault, 2012

阿里巴巴数据技术及产品部.《大数据之路:阿里巴巴大数据实践》, 机械工业出版社, 2017(OneModel/OneID/OneService、ODS-DWD-DWS-ADS)

TDWI Data 101, "The Architectural Disagreement That Still Shapes Data Warehouses"(2026-05,当代视角的两派对比)

BARC / WhereScape, Data Warehouse and Data Vault Adoption Trends(best-in-class 34% vs laggards 15%)

dbt 社区 "Is Kimball dimensional modeling still relevant?"、r/dataengineering 相关讨论(DV 银层 + Kimball 金层的实践共识)

James Serra, "Data Warehouse Architecture – Kimball and Inmon Methodologies"(3NF 表述的标准二手口径)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:Kimball维度建模_vs_Inmon范式建模_深度研究_2026-09-08.md

#维度建模 #Kimball #Inmon #星型模型 #数据仓库 #渠道数据 #智柴

暂无表态

想参与讨论或点赞?登录后使用完整功能

讨论回复(7)

✨
文本版 · 供搜索与朗读

数仓分层设计 深度研究

数据仓库 · 分层架构 · 深度研究

数仓分层设计 深度研究

分层怎么设计,为什么分层?ODS(贴源、原始)→ DWD(明细、清洗/标准化)→ DWS(轻度汇总)→ ADS(应用/报表)+ DIM(维度);好处:复用(一次加工多处用)、隔离(业务变化不波及底层)、血缘清晰、问题定位快。

目 录

一页结论
为什么分层——从没有分层的终局说起
五层逐层拆解
层间纪律:十字结构
分层变体谱系(别被层数吓到)
工程决策
渠道场景完整示例(承接 Kimball 篇)
总监口径逐句评审
反模式清单(分层用错的九种姿势)
参考资料

0. 一页结论

分层的本质是按"变更频率 × 复用范围"给数据加工分级:越往下越稳定、越通用,越往上越多变、越特化。层就是稳定性的分级缓存——ODS 固化"发生了什么"的原始证据,DWD 固化"业务过程的原子事实",DWS 固化"反复使用的规律",ADS 只回答"这个应用此刻要什么"。

四大好处成立,但有一个隐含前提和一个隐含收益:前提是层间纪律(禁绕层、禁回环)——没有纪律的分层只是把存储翻了 N 倍的表演;隐含收益是口径收敛(公共逻辑只写一遍,分层给了口径一个"下沉的落点")和权限分级(敏感清洗在下层完成后,越往上越可开放)。

分层有对价:存储副本 ×层数、链路时延逐层累积、每层都要人维护。层数是治理需求的函数,不是越多越专业——小团队三层就够,七层以上的"层的官僚化"会把收益吃光。

DIM 水平单列是点睛之笔:维度被所有层、所有域引用,放进任何一条垂直链都会造成跨层依赖。垂直数据流 × 水平维度总线 = 十字结构——这正是上一篇 Kimball 总线矩阵与分层架构的接合点。

总监口径是社区标准版,史实上有一处可补充:阿里《大数据之路》原文是三大层 ODS → CDM(Common Data Model,内含 DWD/DWS)→ ADS + DIM;国内流行的"六层 + DWT"是培训体系的实践扩展。口径本身没错,知道出处层级是加分项。

1. 为什么分层——从没有分层的终局说起

想象不分层数仓跑两年后的样子:

报表直接跑在贴源表上,一段"剔除测试订单 + 单位换算"的逻辑在 27 张报表里各写了一遍,其中 4 张写错;

源系统把 order_status 从字符串改成枚举码,全仓 60 个任务连夜排查;

新同事问"GMV 到底信哪张表",没有人答得上来——每张表都"差不多对";

数据对不上时,只能把源系统到报表的所有 SQL 从头读到尾。

分层就是对这个终局的系统性防御。形式化地说:每一层对下游是一个服务契约——"到我这层,某类问题已经被解决,下游不必再关心"。 ODS 解决"从哪来、可否重放",DWD 解决"发生了什么、是否干净一致",DWS 解决"规律是什么、能否复用",ADS 解决"业务要什么"。编译器的中间表示(IR)是同构的思想:每层只做一类变换,任何一层崩了都有明确边界。

1.1 四大收益逐个拆

收益
机制
一句话检验

复用
一段加工只写一遍,多处消费:DWD 一张订单明细喂 N 个 DWS,一个 DWS 喂 N 张 ADS
同一段清洗逻辑在仓里出现 ≥2 次,就该下沉

隔离
源变更(改字段/换系统/脏数据)只波及 ODS→DWD 一段,上层零改动
源系统改字段,要改几张表?答案应该是 1

血缘
依赖单向逐层,血缘天然成无环图,影响分析(改一列动哪些表)机器可算
能一键列出"这列下游有多少消费"

定位
按层二分:数不对先看层——源问题(ODS)、加工问题(DWD/DWS)、口径问题(ADS)
排障时间从"通读全链"降到"二分一轮"

外加两个没写进口径的:口径收敛(公共口径有下沉落点,纠纷在建模阶段裁决——Kimball"一致性维度先行"在分层世界的对应物)和安全分级(脱敏在 DWD 完成,ADS 天然是可开放子集)。

1.2 对价

存储副本随层数翻倍(原子明细 + 各级汇总各存一份);链路时延逐层调度累积(T+1 链路上每层是一个批次);每层都需要建模、调度、监控的维护面。分层是用存储和时延买秩序——买不买、买几层,取决于数据规模和治理需求的交点。

2. 五层逐层拆解

层
全称(通行口径)
一行是什么
回答的问题
常见别称

ODS
Operational Data Store
业务系统数据的原样落地
从哪来?原始长什么样?
贴源层 / 引入层 / raw / bronze

DWD
Data Warehouse Detail
一个业务过程事件一行的干净明细
到底发生了什么?
明细层 / silver

DWS
Data Warehouse Summary
上卷粒度的轻度聚合与主题汇总
有什么稳定规律可复用?
汇总层 / 公共粒度层

ADS
Application Data Service
面向某个具体消费的结果集
这个应用要什么?
应用层 / 集市 / gold / marts

DIM
Dimension
跨层共享的一致性维度
谁、什么、何时、何地?
维度层 / 维表

2.1 ODS 贴源层

原则:不做业务加工。 只做技术动作:落地、类型原样保留、加载去重、增量标记,外加三个技术字段(分区日期、抽取时间、来源系统)。它的价值在溯源与重放——DWD 写错可以随时从 ODS 重算,这就是"隔离"收益的物质基础。

采集三模式:全量覆盖(小表)、全量分区(最常用,每天一份完整快照)、增量 + 拉链(大表流水)。

保留策略:热数据 30–90 天,更早的冷归档(对象存储/湖)。ODS 最占存储,但没有保留策略的 ODS 迟早把仓撑爆。

一个史学细节:ODS(Operational Data Store)在 Inmon 原始语境里是"近实时集成区,供操作型报表",被国内分层语境挪用为"贴源落地"——语义已经反转,读英文文献时别混。

常见错误:在 ODS 里 join、清洗、补字典——等于把 DWD 提前,溯源价值清零。

2.2 DWD 明细层

定位:一行 = 一个业务过程事件(呼应 Kimball 四步法的粒度声明)。事实表的家就在这层。

五类动作:清洗(去重、补默认、剔测试单)、标准化(单位/币种/时区/枚举统一)、一致化(OneID 归一,自然键替换为 DIM 代理键)、结构化(源系统大宽表拆成过程化事实)、退化维保留(单据号留事实表)。

纪律:不在这层做跨业务过程的聚合;一行破坏粒度,下游全链口径悬空。

常见错误:DWD 变成 ETL 中转垃圾场——临时逻辑、半成品表堆在 dwd_ 前缀下,血缘图上 DWD 层一团乱麻。

2.3 DWS 汇总层

定位:上卷粒度的可复用聚合(如 经销商 × 品类 × 天)。存在理由只有一个:复用率。"日汇总"被十几个报表反复重算,就提前物化一次。

下沉判据(rule of three):一个汇总口径被 ≥3 个下游场景使用 → 下沉 DWS;只有 1 个消费 → 留在 ADS。

两种形态:聚合事实表(干净,粒度上卷)与主题宽表(多过程拼一行,方便取数但破坏粒度纯度——要克制)。

常见错误:DWS 按"某张报表的需要"定制——那是 ADS 的活,DWS 定制化等于把复用层变成私产。

2.4 ADS 应用层

定位:面向具体消费物——报表、大屏、接口、标签包、实验看板。允许冗余、允许反范式、按查询模式建物化/索引;口径已冻结,生命周期跟随应用。

常见错误:① ADS 直读 ODS(绕层,血缘断裂,双倍清洗);② 公共口径沉淀在 ADS 里(被第二个应用引用时才发现要"上提",返工)。正确的流向是公共口径往下沉,应用需求往上长,DWS/ADS 边界随复用率动态调整。

2.5 DIM 维度层

为什么水平单列:商品、经销商、日期这些维度被 DWD/DWS/ADS 全层引用。塞进任何一条垂直链,另一个域要用就得跨层跨域依赖——所以水平切出来,人人可引,一处维护。

这就是上一篇的一致性维度 + 总线矩阵在分层世界的化身:总线矩阵管"哪些维度要一致",DIM 层给它们一个物理的家。

管理内容:SCD 策略、主数据/OneID、层级与上卷路径、枚举字典。DIM 的质量上限 = 整个数仓的口径上限(主数据乱,星型和分层一起塌)。

3. 层间纪律:十字结构

flowchart TB
ODS[ODS 贴源] --> DWD[DWD 明细]
DWD --> DWS[DWS 汇总]
DWS --> ADS[ADS 应用]
DIM[DIM 公共维度] -.-> DWD
DIM -.-> DWS
DIM -.-> ADS

三条铁律:

依赖单向、逐层、无环:只允许 N → N+1(DIM 是水平例外,可被 DWD 及以上引用)。

"止于"任何层合法,"跳过"任何层非法:消费端可以直接查 DWD(明细查询场景天经地义);但加工依赖不许跳——ADS 任务的输入不许是 ODS。

公共口径只准下沉,不准旁落:同一段逻辑出现第二处,就该讨论它属于哪一层,而不是复制粘贴第三处。

4. 分层变体谱系(别被层数吓到)

体系
分层
备注

Inmon CIF(1991)
ODS → EDW → DM
ODS 原意是近实时集成区;EDW 内部按主题域 3NF 建模

经典三层(口语)
ODS → DW → DM
国内早期教科书口径,DW 内不分明细/汇总

阿里《大数据之路》原文
ODS → CDM(内含 DWD/DWS)→ ADS + DIM
三大层;CDM = Common Data Model 公共层;官方文档(MaxCompute/DataWorks)至今如此表述

社区六层(培训体系)
ODS → DWD → DWT → DWS → ADS + DIM
DWT(累计型主题宽表,如用户首次/末次行为大宽表)为实践扩展,尚硅谷课程推广后流行

湖仓 medallion
bronze → silver → gold
bronze≈ODS,silver≈DWD+DIM,gold≈DWS+ADS;Databricks 推广

dbt 实践
staging → intermediate → marts
同构思想的视图版:staging 轻(重命名/轻洗),marts 即 ADS

实时数仓
Kafka 贴源 → Flink DWD(清洗/join)→ 状态聚合 DWS → OLAP/服务 ADS
分层逻辑同构,载体换成流;lambda/kappa 不改变层的职责定义

要点:层数和名字从来不是重点,"按变更频率与复用范围分级加工"才是。 各家体系是同一思想在不同载体(离线/流/湖)上的投影。面试或评审场合能把任意两套体系互译,比背层数有用得多。

5. 工程决策

5.1 层数怎么定

团队状态
建议
升级触发点

单人/小团队、几条管道
ODS → DWD → ADS 三层(汇总先用视图顶着)
同一聚合被第 3 处引用

多业务线、多消费方
五层全配 + DIM
出现口径纠纷 / 源系统频繁变更

超大域、多团队并行
再加 STG(落地中转)与 TMP(临时表治理),DWT 按需
—

反着说:不要为了简历兼容性加层。每加一层都是一批命名规范、调度作业和监控面,七层以上的链路时延和运维成本会把收益吃光。

5.2 规范落位(示例)

规范
约定示例

表命名
{层}_{主题域}_{内容}_{粒度}_{周期}:ods_dms_receipt_inc、dwd_sales_order_di、dws_sales_dealer_1d、ads_dealer_daily、dim_product

分区
事实表按天 ds;拉链表按执行日;维表全量快照+分区

生命周期
ODS 热区 30–90 天后归档;DWD 全量永久;DWS 全量;ADS 跟随应用下线

Owner
每张表有唯一 owner 团队;DIM 归主数据团队,不许各域私拷

5.3 数据质量挂在哪层

层
检查
例子

ODS
量级、主键、到达率
今日行数环比跌幅 >30% 告警;单据号重复

DWD
空率、值域、外键命中
金额空率、枚举超集、事实表键在 DIM 找不到

DWS
波动、勾稽
同比环比突跳;汇总和 ≠ 明细重算和

ADS
口径一致性
ADS 结果 vs DWS 重算双跑比对

原则:每层的问题在本层拦截——DWD 的键残缺不允许流到 DWS 才被发现,否则"问题定位快"这条收益自动作废。

5.4 与成本的关系

复用是省计算,分层是多花存储——两者按存储/计算价差与查询频次算账。现代引擎(物化视图、缓存、按需物化)正在压低 DWS 的物化必要性,但只要"同一口径多处消费"仍然存在,分层的治理收益就不随引擎演进贬值。

6. 渠道场景完整示例(承接 Kimball 篇)

flowchart TB
S1[DMS 进货] --> O1[ods_dms_receipt]
S2[ERP 出库] --> O2[ods_erp_so]
S3[POS 小票] --> O3[ods_pos_ticket]
O1 --> W1[dwd_procurement_receipt]
O2 --> W2[dwd_channel_sales]
O3 --> W3[dwd_store_pos]
DIM[DIM 经销商 终端 商品 日期 组织 促销] -.-> W1
DIM -.-> W2
DIM -.-> W3
W1 --> S4[dws_dealer_sku_1d]
W2 --> S4
W2 --> S5[dws_inventory_dealer_1d]
W3 --> S6[dws_store_sku_1d]
S4 --> A1[ads_dealer_daily 经销商日报]
S5 --> A1
S6 --> A2[ads_channel_kanban 渠道看板]
S5 --> A3[库存健康接口]

拿一个指标走完全程:渠道库存周转天数 = 日均库存 ÷ 日均出库。

层
这层发生什么

ODS
dms 进货单、erp 出库单、库存日结原样落地,带分区与来源标记

DWD
进/出事务事实(一行=单据行项,OneID 归一、单位统一、冲账保留负数行);库存周期快照事实(经销商×仓×商品×日)

DIM
经销商/商品维 SCD2,保证"按当年等级看当年库存"的历史正确

DWS
dws_inventory_dealer_1d 存期末库存(粒度已是每日一行,AVG 跨行合法);dws_channel_sales_1d 存日出库量

ADS
周转天数 = 近 30 日 AVG(期末库存) ÷ 近 30 日 AVG(日出库),双口径物化进日报

排障演示:某经销商周转天数突然翻倍。按层二分——ADS 公式改动?(无)→ DWS 库存日均里哪天突刺?(9-01 库存 ×8)→ DWD 快照当天多了一笔?(一笔 8000 件的入库单)→ ODS 溯源:经销商 9-02 补传的 8-31 单据,DWD 冲账语义把它记进了当日。四步定位,每步只看一层——这就是"问题定位快"的实指。

7. 总监口径逐句评审

原话
评审

"ODS 贴源、原始"
✅ 且要补一句纪律:贴源层禁业务加工,它的全部价值在溯源与重放

"DWD 明细、清洗/标准化"
✅ 补:粒度声明先行(一行=一个业务过程事件),清洗/标准化/一致化/结构化四件套,呼应 Kimball 四步法

"DWS 轻度汇总"
✅ 但没给边界判据——判据是复用率(rule of three),否则 DWS 会退化成"报表定制层"

"ADS 应用/报表"
✅ 补:公共口径发现被多处引用要下沉,ADS 只做裁剪与呈现

"DIM(维度)单列"
✅ 点睛。可以点破:DIM 层就是 Kimball 一致性维度/总线矩阵的物理化身,两套话语体系在这里接合

"复用/隔离/血缘/定位"四大好处
✅ 补两个隐含项:口径收敛、权限分级;再补一个前提:层间纪律——禁绕层禁回环没有治理兜底时,四大好处全部落空

(缺失)成本项
存储副本×层数、链路时延、维护面——分层是买秩序的价钱,报告里应如实报价

8. 反模式清单(分层用错的九种姿势)

绕层引用:ADS 直读 ODS——血缘断裂,清洗逻辑双份。

层内倒挂/回环:DWS 依赖 ADS,或下游产出反哺上游。

ODS 做加工:贴源层里 join 清洗,溯源价值清零。

DWD 垃圾场:临时表、半成品堆进 dwd_ 前缀。

DWS 私产化:按单张报表定制汇总层。

ADS 承载公共口径:第二个消费者出现时被迫返工上提。

DIM 各域私拷:五个域五份商品维,口径回到史前。

无限加层:七层以上,时延与运维吃掉全部收益。

无生命周期:ODS 只进不出,存储成本指数漂移。

9. 参考资料

阿里云官方文档《数仓分层》(MaxCompute/DataWorks):ODS → CDM(DWD/DWS)→ ADS 三大层的原文表述 —— help.aliyun.com/zh/maxcompute/getting-started/divide-a-data-warehouse-into-layers

阿里巴巴数据技术及产品部.《大数据之路:阿里巴巴大数据实践》,机械工业出版社,2017(OneData 体系、数据分层与规范原始出处)

Kimball, Ross.《The Data Warehouse Toolkit》3rd ed., 2013(粒度声明、一致性维度——DWD 层与 DIM 层的方法论根基)

Inmon.《Building the Data Warehouse》4th ed., 2005(CIF 三层与 ODS 的原始语境)

Databricks medallion architecture 文档(bronze/silver/gold 与传统分层的对应)

dbt Labs, "How we structure our dbt projects"(staging/intermediate/marts 分层同构)

尚硅谷大数据课程讲义(DWT 层的流行源头,社区六层变体)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:数仓分层设计_深度研究_2026-09-08.md

#数仓分层 #ODS #DWD #DWS #ADS #DIM #数据治理 #智柴

暂无表态
✨
文本版 · 供搜索与朗读

缓慢变化维 SCD 深度研究

数据仓库 · 维度建模 · 深度研究

缓慢变化维 SCD 深度研究

缓慢变化维 SCD?Type 1 直接覆盖(不要历史);Type 2 拉链表(加行 + start_date/end_date,保留全部历史——渠道审计刚需);Type 3 加备用列(只留上一版)。

目 录

一页结论
问题定义:维度为什么会"变",为什么这是问题
Type 全家福:先全景再重点
Type 1:覆盖
Type 2:拉链表(重点)
Type 3:备用列
进阶:Type 4 / 6 / 7(渠道大表会碰到)
替代与补充方案
渠道场景 SCD 策略表(承接前两篇)
选型决策树
总监口径逐句评审
反模式清单
参考资料

0. 一页结论

SCD 的本质是一个矛盾的管理:源系统只存"当前状态"(ERP 里经销商等级就一列,改了就没了),而分析要"按当时的口径统计当时的事实"。SCD 就是给属性的版本建时间轴,让事实行指向"当时那个版本"。先问业务"要不要按当年的状态看当年",再选 Type——SCD 是业务历史正确性需求的翻译,不是技术偏好。

Type 1/2/3 不是平级三选项:Type 2 是唯一保留完整历史的方案(所以是审计刚需的唯一解);Type 1 是"历史不重要/纯纠错";Type 3 只够"对比上一版"。实务里 90% 的业务属性走 Type 2,Type 1 用于纠错,Type 3 罕用。

总监口径的一个史学补充:"Type 2 = 拉链表"在国内成立,但"拉链表(加行 + start_date/end_date)"是国内 Hive 时代的工程化叫法,Kimball 原版 Type 2 的表述是"新行 + 有效期 + 当前行标记",本质相同;国内工程实践还加了"每日全量快照比对"这套加工套路,比教科书版更具体。

SCD 的成败在工程细节,不在选型:变化字段白名单(哪些字段触发拉链、哪些随行覆盖)、日期区间约定(闭区间还是左闭右开)、迟到数据回填、断链/重叠校验——四个细节任何一个含糊,拉链表都会在半年内变成口径事故源。

不是所有变化都该 SCD:度量放事实表(采购价、金额),不放维度;纯纠错走 Type 1;高频变化的数值属性(信用分档)用 Type 4 迷你维拆出去——全字段拉链是维表爆炸的头号原因。

1. 问题定义:维度为什么会"变",为什么这是问题

维度是业务实体的描述,而业务系统通常只维护当前值。经销商 2024 年是银牌,2025-07 升金牌——ERP 里 UPDATE 一下,银牌时代从数据库里消失了。

但事实在累积:2025 年 5 月的出库单确实发生在"银牌时期"。如果维度被覆盖,今天重跑去年报表,这张单子会挂到金牌下面——历史被静默重写,没有任何报错。

SCD 的形式化解法:维度表从"一实体一行"变成"一实体一版本一行",每行带有效期;事实表的外键不再指向实体,而是指向实体在事实发生时刻的那个版本(代理键)。

"缓慢"的含义:变化频率远低于事实(一年几次,不是每秒千行),但必然会变(等级、区域、层级、组织、状态)。

2. Type 全家福:先全景再重点

Type
机制
保留的历史
何时用

0 原样保留
属性永不改
全部
日期维属性、出生日期类"事实型"属性

1 覆盖
UPDATE 原行
无
纯纠错、无分析价值属性

2 加行
新行 + 有效期 + 当前行标记
全部
等级/层级/归属/状态——主力

3 备用列
加 previous 列
仅上一版
只需"变前 vs 变后"、变化少而可预期

4 迷你维
高频变化属性拆小维表,事实表双外键
全部(按版本)
数值型、变化频繁(信用分档)

5
Type 4 + 主维镜像当前值
全部
要"当前口径查询不 join"

6
Type 1+2+3 混合:拉链行内镜像"最新值"
全部 + 双视角
要"按最新口径重看全部历史"

7
事实表双外键(时点键 + 当前键)
全部
同 6,在事实层解

实操分布:0/1/2 覆盖 95% 的场景;Type 3 罕见;4/6 是渠道大表和集团型组织的进阶选项。

3. Type 1:覆盖

机制:变化时直接 UPDATE 原行,不留痕迹。

适用:① 纠错(源系统的错字、错码);② 无分析价值的属性(备注、联系人手机);③ 保证常量不变的属性。

纪律:必须在维度口径文档里显式标注每个字段是 Type 1 还是 Type 2;Type 1 字段变更后要级联刷新上层——聚集表、DWS 宽表里如果冗余了这个属性,不同步就是上层口径与维表脱节(最常被忘记的一步)。

陷阱:把有业务含义的属性(等级、归属)当 Type 1 处理,历史报表悄悄漂移、且无法察觉——最阴险的口径事故。

4. Type 2:拉链表(重点)

4.1 机制

变化时旧行"封口"(end_date 收口、is_current=0),插入新行(start_date=生效日、is_current=1、新代理键)。事实表通过代理键自然落到"当时那个版本"。

经销商 D001 等级 银牌→金牌,拉链后:

dealer_sk
dealer_id
grade
start_date
end_date
is_current

1001
D001
银牌
2024-01-01
2025-06-30
0

1207
D001
金牌
2025-07-01
9999-12-31
1

2025-05 的出库单挂 dealer_sk=1001(按银牌统计),2025-08 的挂 1207(金牌)。任何一天的事实都能按当时的等级/区域/层级复算——这就是"渠道审计刚需"的实现原理。

4.2 表结构(示例 DDL)

CREATE TABLE dim_dealer (
dealer_sk BIGINT PRIMARY KEY, -- 代理键,事实表引用它
dealer_id VARCHAR(32), -- 自然键(源系统编码)
dealer_name VARCHAR(128), -- Type 1 纠错随行覆盖
grade VARCHAR(16), -- SCD2 触发拉链
region VARCHAR(32), -- SCD2
status VARCHAR(8), -- SCD2(终止状态照常拉链)
settle_account VARCHAR(64), -- Type 1
start_date DATE,
end_date DATE,
is_current TINYINT,
version INT -- 同一自然键内的版本号
);

4.3 加工套路(国内工程化:每日快照比对)

-- ① 封口:源快照与当前链逐字段比对(SCD2 白名单字段),变了的封口
UPDATE dim_dealer SET end_date = '<今日-1>', is_current = 0
WHERE is_current = 1 AND dealer_id IN (SELECT dealer_id FROM 变化集);

-- ② 开链:变化集插入新行
INSERT INTO dim_dealer
SELECT 新代理键, s.dealer_id, s.dealer_name, s.grade, s.region,
s.status, s.settle_account,
'<今日>', '9999-12-31', 1, 旧.version + 1
FROM 源快照 s JOIN 变化集 USING (dealer_id);

三个工程约定必须全仓统一(是拉链表事故的三大源头):

区间约定:[start_date, end_date] 闭区间还是 [start, end) 左闭右开——join 条件(d.start_date <= f.date AND f.date < d.end_date 还是 <=)完全不同,混用即错。上表用的是"end_date=生效日-1"的闭区间惯例。

变化字段白名单:哪些字段触发拉链(grade/region/status)、哪些随行 Type 1 覆盖(dealer_name/settle_account)、哪些忽略(ETL 技术字段)。白名单没定义清楚,要么维表爆炸,要么该拉链的没拉。

9999-12-31 还是 NULL 当"未封口":NULL 会让 join 和索引行为变复杂,实践推荐哨兵日期。

4.4 完整性校验(DIM 层 DQC)

无断链无重叠:每个自然键在任意日期恰有一行(按日自 join 相邻版本检查连续性)。

is_current 唯一:每个自然键至多一行 is_current=1,且 end_date=哨兵值。

变更数核对:当日封口数 + 新开链数 = 源系统变更数(对不上说明比对逻辑或源抽取出了问题)。

4.5 三个深坑

迟到数据回填:8 月发生的等级变更 9 月才同步。三选一:按生效日拆链回填(历史正确,工程重,要重刷 8 月之后的事实外键);按加载日开链(快,但 8-9 月的事实挂错版本);拆链回填 + late_arriving 标记(推荐,渠道手工单据多的场景必须回答这个问题)。

维表膨胀:变化频繁的实体会一年拉出几百个版本。对策:只对关键字段拉链(表内混合 Type 1+2)、或拆 Type 4 迷你维。

性能:消费端永远 join 代理键而不是自然键 + 日期区间(运行时按日期区间 join 是应急回溯手段,不是给 BI 用户的设计)。

5. Type 3:备用列

机制:加 previous_grade 列,变化时旧值挪入、新值覆盖,行数不变。

适用:只关心"上一次是什么"、变化少且可预期的场景——部门一次性合并、区域重划、品牌更名(迁移期做"新旧口径对比"报表)。

为什么罕用:只能记一版;字段数随版本数膨胀;业务几乎总在要全历史。Type 3 的真实身份是"轻量折中",不是 Type 2 的替代品。

6. 进阶:Type 4 / 6 / 7(渠道大表会碰到)

Type 4 迷你维:把高频变化的数值型/标志型属性(经销商信用分档、终端活跃度分层、客户生命周期阶段)拆成小维表,事实表同时挂主维键和迷你维键。主维表保持稳定(只剩 Type 1/低频 Type 2),膨胀压力转移给小表。

Type 6(1+2+3 混合,Kimball 社区定型的套路):拉链行内额外镜像"当前值"列(current_grade)。同一行里有三个视角:版本值(grade,当时是什么)、有效期(拉链)、当前值(current_grade,现在是什么)。回答"按最新组织口径重看全部历史"(集团重组后按新大区重算历史销量)就是它的主场。

Type 7 双外键:同一问题在事实层解——事实表同时存"时点代理键"和"当前代理键",查询时选键。事实表变宽、存储涨,换来 join 简单。

升级触发点:维表千万行级,或"按最新口径重看历史"成为高频正式需求(不再是临时回溯)。

7. 替代与补充方案

方案
做法
优点
代价

全量日期快照维
每日一份完整维表快照,事实按日期 join 当日快照
实现 5 分钟、永不断链、审计友好
存储 ×365,规范弱

事实存自然键 + 查询时区间 join
维度拉链,事实不动自然键,查询时按事实日期落到区间
事实表轻
join 条件复杂,BI 用户易错;只适合临时回溯

变化建模为事实
组织调整/等级变更本身建事件事实表
变更历史可分析(多久升一次级)、事件审计友好
设计成本,双轨治理

与主数据/OneID 的分工:OneID 管"实体归一"(同一经销商多处编码归一),SCD 管"属性版本"(归一后的实体怎么随时间变化)——两者正交,都归属 DIM 层治理。

8. 渠道场景 SCD 策略表(承接前两篇)

维度
属性
策略
理由

dim_dealer
经销商编码/名称
Type 1
编码不变;名称纠错覆盖

等级、服务大区、合作状态
Type 2
审计复算返利/费效的历史依据

信用分档(月度刷新)
Type 4 迷你维
高频数值型,拉链会爆炸

dim_store
业态、归属经销商
Type 2
归属变更直接影响渠道归属历史

门店地址
Type 1
分析价值低

dim_product
品类归属
Type 2
品类报表的历史正确性

条码/规格/单位换算
Type 1
纠错性质

采购价、供货价
不放维度
是度量,进事实表

dim_date
全部
Type 0
永不修改

dim_org
组织架构
Type 2 + Type 6 镜像
重组后按新架构重看历史

审计场景演示:审计要求"2025 年 Q1 各经销商按当时返利等级复算返利"。事实表的 dealer_sk 直接命中当时版本,一条 join 完成。反例:若当年等级走了 Type 1,这个需求在物理上不可满足——只能翻纸质档案。

9. 选型决策树

flowchart TD
A[维度属性要变了] --> B{历史口径要正确吗}
B -- 不要 纯纠错 --> T1[Type 1 覆盖]
B -- 只要对比上一版 --> T3[Type 3 备用列]
B -- 要全历史 --> C{变化多频繁}
C -- 偶发 常规属性 --> T2[Type 2 拉链]
C -- 高频 数值型 --> T4[Type 4 迷你维]
B -- 要按最新口径重看全部历史 --> T6[Type 6 混合镜像]

10. 总监口径逐句评审

原话
评审

"Type 1 直接覆盖(不要历史)"
✅ 补:只适用于纠错与无分析价值属性;把业务属性当 Type 1 是最阴险的事故;Type 1 变更要级联刷新上层冗余

"Type 2 拉链表(加行 + start/end_date,保留全部历史——渠道审计刚需)"
✅ 完全成立。"拉链表"是国内工程叫法(Kimball 原版:新行+有效期+当前标记);审计刚需的实现原理 = 事实挂代理键落到当时版本;成败在四个工程细节:白名单、区间约定、迟到回填、断链校验

"Type 3 加备用列(只留上一版)"
✅ 但实务罕用,是窄场景折中而非 Type 2 替代品

(缺失)Type 0
日期维属性原样保留,是全家福里最安静的一档

(缺失)Type 4 迷你维
渠道高频数值属性(信用分档)的防爆方案

(缺失)"不是所有变化都该 SCD"
度量进事实不放维度;全字段拉链是维表爆炸头号原因

11. 反模式清单

全字段拉链:维表一年膨胀百倍,join 成本失控。

无变化字段白名单:该拉的没拉(口径漂移)、不该拉的乱拉(爆炸)。

区间约定含糊:闭/开区间混用,日期 join 差一天,报表数字对不上。

迟到变更不拆链:历史版本挂错,审计复算失真。

Type 1 变更不刷上层:聚集表/宽表与维表脱节。

消费端按自然键+日期区间 join 拉链表:把运行时回溯当正式设计。

无断链/重叠校验:拉链表坏了没人知道,直到审计上门。

把度量放维度再拉链:价格反复变化把商品维拉成瀑布。

12. 参考资料

Kimball, Ross.《The Data Warehouse Toolkit》3rd ed., Wiley, 2013,第 5 章(SCD 各 Type 的权威定义与迷你维/桥接细节)

kimballgroup.com Design Tips(Type 2 实操、Type 6 混合套件的社区推广文章)

阿里巴巴数据技术及产品部.《大数据之路》2017(DIM 层治理、拉链表工程实践语境)

国内 Hive/MaxCompute 拉链表工程实践(每日全量快照比对 + 封口/开链两段式加工,多个云厂商文档同构)

本系列前两篇:《Kimball维度建模_vs_Inmon范式建模_深度研究_2026-09-08》《数仓分层设计_深度研究_2026-09-08》(DIM 层与 SCD 的上下游关系)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:缓慢变化维SCD_深度研究_2026-09-08.md

#SCD #缓慢变化维 #拉链表 #维度建模 #数据仓库 #渠道数据 #智柴

暂无表态
✨
文本版 · 供搜索与朗读

事实表三种类型 深度研究

数据仓库 · 维度建模 · 深度研究

事实表三种类型 深度研究

事实表三种类型?事务事实表(一条流水一行,如下单/出货)、周期快照事实表(每天一份库存快照——渠道库存就是这个)、累积快照事实表(一个业务流程一行,带里程碑时间)。

目 录

一页结论
为什么分三种:粒度决定一切
事务事实表(transaction fact)
周期快照事实表(periodic snapshot)
累积快照事实表(accumulating snapshot)
第四种:无事实事实表(factless fact)
对比总表与选型
渠道场景三表联动(承接系列前三篇)
总监口径逐句评审
反模式清单
参考资料

0. 一页结论

三种类型的本质区别是一句话:"一行代表什么"。事务=一次业务事件;周期快照=某周期结束时的一个状态;累积快照=一条流程的整段生命周期。这是 Kimball 四步法第二步"声明粒度"的具体化——粒度声明先于一切设计,粒度说不清的事实表必然口径爆炸。

可加性是分水岭:事务事实全可加;周期快照半可加(库存可跨经销商/商品求和、不可跨日期求和);累积快照的持续时间度量不可加(只能平均/分布)。报表引擎必须按类型拦截非法聚合。

总监口径全对,且"渠道库存就是周期快照"点得准。要补两点:还有第四种"无事实事实表"(覆盖/资格关系,渠道的活动覆盖就是);三种类型不是三选一,同一业务过程经常两三种并存(销售:事务表管明细 + 累积快照管履约时效)。

选型的唯一驱动力是业务过程的数据形态:离散事件流→事务;周期性状态→快照;有起点终点的流程→累积。先看形态再建表,而不是从报表倒推。

工程差异比名字差异更重要:事务表只插入;周期快照整批周期插入、必须与事务表勾稽(期末=期初+进−出);累积快照是唯一持续 UPDATE 的事实表,要处理里程碑乱序与回退。

1. 为什么分三种:粒度决定一切

事实表设计的第一步不是列字段,是回答"一行是什么"。三种类型就是三种答案:

一条流水一行——世界由事件组成,每发生一次记一次;

一个周期一张照片——有些东西不是"发生",是"存在着"(库存、余额),只能按期盘点;

一个流程一行——有些分析对象是"过程"(履约、退货、审批),关心的是整段旅程。

判定流程看数据形态:业务过程是离散事件流、周期性状态、还是有起点终点的流程?先看形态再建表。同一业务过程可以多表并存——订单既有事务事实(每次下单/修改/取消各一行,管明细分析)又有累积快照(一单一行,管履约时效),两张表不冗余,回答的是不同问题。

2. 事务事实表(transaction fact)

机制:一次业务事件一行,事件发生时插入,基本不再更新。行数 = 事件数。

度量:数量、金额,全可加,任意维度切片求和都合法。

渠道例子:进货单行项、销售出库行项、POS 小票行项、费用核销行项。

变体:粒度可以窄到单据行项,也可以是更细的事件(点击、呼叫、扫码)。

设计要点:① 退货行用负数事实(reversal 语义),禁止物理删除——渠道手工单据多,冲账是常态;② 晚到事件(单据补传)按业务日期落分区,配 late_arriving 标记;③ 多币种/多单位在行级标准化(最小销售单元、不含税/含税列并存)。

3. 周期快照事实表(periodic snapshot)

机制:每个固定周期(日/周/月)末,对状态类实体拍一张照。一行 = 一实体 × 一周期。行数 = 实体数 × 周期数。

度量:库存量、余额、账户状态——半可加:跨实体求和合法,跨周期求和非法。这是三种类型里唯一自带"聚合禁区"的。

存在理由:① 状态本身是分析对象(库龄、周转、停留天数)——状态无法从流水推算(期初库存哪来?);② 把反复使用的事务聚合提前固化(日销量 ×日快照);③ 补齐稀疏(没卖货的门店也有"库存为零"这个状态)。

渠道例子:经销商 × 商品 × 日库存快照;客户 × 月应收余额快照。

快照表与事务表的勾稽是库存数仓最值钱的 DQC:

本期期末库存 = 上期期末库存 + Σ本期进货 − Σ本期出货 ± 盘盈亏调整

每天跑一遍,对不上就说明进、出、存三条链有一处断了(漏单、重复、冲账语义错)。

设计要点:① 快照时点统一(日末 24 点 vs 营业结束,时区写进口径);② 全量快照 vs 稀疏快照——渠道建议全量("零库存"也是状态,动销率要靠它),但行数 = 经销商×SKU×天,存储要先算账;③ 跨期求和拦截要落在 BI/语义层,不能指望用户自觉;④ 快照任务漏跑一天 = 断链,要有日历完整性监控。

4. 累积快照事实表(accumulating snapshot)

机制:一个业务流程实例一行(一张订单、一次退货申请),随流程推进反复 UPDATE,带一排里程碑日期外键和里程碑间隔度量。行数 = 流程实例数。

它是三种类型里唯一"持续更新"的事实表——事务表只插入,周期快照整批插入,累积快照是活物。

渠道例子:订单履约(下单→审单→发货→签收→开票→回款)、退换货流程、经销商准入、费用核销流程。

一张订单的累积快照行(示例):

order_no
下单
审单
发货
签收
回款
下单→签收(天)
当前状态

SO1024
08-01
08-01
08-03
08-06
09-05
5
已结清

SO1025
08-29
08-30
—
—
—
—
发货中

度量:数量金额可加;间隔天数不可加(跨流程求和无意义,只能 AVG/分位数/超时率)。

设计要点:① 每个里程碑角色扮演一个日期维度,禁止字符串日期;② 流程没走完,未到的里程碑置 NULL 且另设 current_status 维度——别造伪日期;③ 里程碑乱序与回退:签收早于发货是数据问题或真实退货,校验规则要预先定义;已完结流程"重开"(客户拒收退回在途)时快照行要能回到在途状态;④ UPDATE 频率高,下游依赖要容忍行级变化(对比:事务表追加式下游最好做)。

变体:里程碑多到几十个时,考虑"一里程碑一行"的事件式事实表,或事务+累积双轨(事实细节归事务表,累积快照只留里程碑与关键数量)。

5. 第四种:无事实事实表(factless fact)

没有度量,只有外键组合——"关系发生过"本身就是信息:

促销覆盖:哪天、哪个门店、参加了哪个活动(渠道最常用);

经销商 × 品牌代理授权、终端 × 业务员拜访登记;

经典例子:学生选课、广告排期。

用途:count 即分析——"有活动覆盖门店的销售额 vs 无覆盖门店"就是覆盖表 join 事务表。配日期维(覆盖有效期),多对多关系则与桥接表配合(呼应第一篇)。

6. 对比总表与选型

维度
事务
周期快照
累积快照
无事实

一行
一次事件
实体×周期的状态
一条流程实例
一次关系/资格

更新方式
只插入
周期性整批插入
持续 UPDATE
插入/到期封口

可加性
全可加
半可加
间隔类不可加
无度量(count 可加)

行数驱动
事件数
实体数×周期数
流程实例数
关系数×有效期

渠道例子
出库单行项
经销商日库存
订单履约
活动×门店覆盖

选型问三个问题:业务过程是离散事件、周期性状态,还是有终点的流程?状态需不需要按期盘点?流程时效要不要分析?——多数业务过程只天然属于一种类型,猜不出来的时候回到源系统看数据形态。

常见并存组合:销售 = 事务 + 累积快照;库存 = 周期快照 + 进出事务(勾稽);促销 = 无事实 + 事务。

7. 渠道场景三表联动(承接系列前三篇)

五个业务过程、四种事实类型一次配齐:进货(事务)、出库(事务)、库存(周期快照)、履约(累积快照)、活动覆盖(无事实)。三个典型指标各踩一种类型:

库存周转天数(周期快照):近 30 日 AVG(期末库存) ÷ 近 30 日 AVG(日出库)——AVG 合法因为快照粒度已是每日一行;SUM 30 天库存则荒谬。

履约时效(累积快照):下单→签收 AVG 天数、超时率、各里程碑停留分布——持续改进供应链的仪表盘。

动销率(事务 × 快照):有销量门店数 ÷ 有库存门店数——没卖货的门店必须存在于库存快照里,这个指标才算得出来。

费效比(事务 × 无事实):活动核销费用 ÷ 覆盖门店增量销售额——覆盖表是分母的资格集。

8. 总监口径逐句评审

原话
评审

"事务事实表(一条流水一行,如下单/出货)"
✅ 补:只插入不更新、全可加;退货负数行与冲账语义要预先设计

"周期快照事实表(每天一份库存快照——渠道库存就是这个)"
✅ 点得准。补:半可加(跨期求和非法,要 BI 层拦截);必须与事务表勾稽(期末=期初+进−出);快照时点与全量/稀疏选择

"累积快照事实表(一个业务流程一行,带里程碑时间)"
✅ 补:它是唯一持续 UPDATE 的事实表;里程碑乱序/回退与"重开"语义要预先定义;间隔度量不可加

(缺失)第四种
无事实事实表:覆盖/资格关系,渠道的活动覆盖、代理授权都是

(缺失)粒度声明
"一行是什么"先于一切列设计——三种类型本身就是三种粒度声明

(缺失)并存
三种类型不是三选一,同一业务过程按分析需要并存(销售=事务+累积)

9. 反模式清单

用事务表推算库存:状态无法从流水推算(期初哪来),必须建快照。

跨周期求和快照:30 天库存 SUM 出来的数字毫无意义还常被写进周报。

累积快照粒度不清:同一订单出现两行(重开时插新行不封旧行)。

把明细塞进累积快照:行项级明细归事务表,累积快照只留里程碑与关键数量。

快照漏跑无监控:库存日历断一天,周转和库龄全月失真。

三种混成一张超级宽表:粒度即崩,三种更新语义互相打架。

里程碑存字符串日期:没有角色扮演日期维,算间隔要靠字符串截取。

10. 参考资料

Kimball, Ross.《The Data Warehouse Toolkit》3rd ed., Wiley, 2013,第 3 章(事实表基础与三种类型定义)及零售/库存等行业章节(周期快照勾稽的经典出处)

kimballgroup.com Design Tips(事实表类型、累积快照设计技巧)

本系列前篇:《Kimball维度建模_vs_Inmon范式建模》(四步法与粒度声明)、《数仓分层设计》(三种事实表在 DWD/DWS 的落位)、《缓慢变化维SCD》(维度侧的时间语义,与事实表侧互补)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:事实表三种类型_深度研究_2026-09-08.md

#事实表 #事务事实表 #周期快照 #累积快照 #维度建模 #渠道数据 #智柴

暂无表态
✨
文本版 · 供搜索与朗读

拉链表实现 深度研究

数据仓库 · 维度建模 · 深度研究

拉链表实现 深度研究

拉链表怎么实现?当日增量与现有开链数据 full outer join:新增 → 开链;变化 → 旧记录关链 + 新记录开链;不变 → 保持。核心 SQL:row_number() 判新旧 + end_date = '9999-12-31' 表示开链。

目 录

一页结论
问题重述:为什么"实现"是独立课题
标准实现:当日增量 × 开链行 比对(完整 SQL)
加工数据流
row_number 的第二处用法:写出幂等
实现路线对比
工程细节与坑(按杀伤力排序)
现代引擎速写
渠道场景落地(承接系列)
总监口径逐句评审
反模式清单
参考资料

0. 一页结论

拉链表加工只有一个原语:把"当日增量 vs 现有开链"的差异变成四个动作——新增开链、变化关链+开链、不变保持、消失关链。总监口径的三动作是对的,漏了第四个:源里删掉的键(增量流水世界少见、全量快照比对世界常见)要关链 + 软删标记,不能物理删除。

FULL OUTER JOIN 必须只 join 开链行(end_date='9999-12-31')。忘加这个过滤,历史版本会参与比对,每个历史版本都被判成"变化",链当天爆炸——这是拉链表事故第一名。

row_number() 有两处标准用法:① 当日增量流水先按自然键取末状态(一天多变只留最晚一条,拉链粒度=天);② 写出时保证每个自然键唯一胜出行(重跑幂等)。这就是"判新旧"的实指。

NULL-safe 比较是"假不变"的根源:Hive 里 NULL <> '金牌' 结果是 NULL 不是 TRUE,CASE 会把它落进 ELSE 判成"不变"——变化静默漏判。白名单字段逐个 nvl() 包住(或存 attr_md5 冗余列比对)。

全量重写(INSERT OVERWRITE)天然幂等,重跑翻倍这类事故只在"追加式"实现里发生。校验三件套(断链、重叠、is_current 唯一)是拉链作业的最后一步,不是可选项。

1. 问题重述:为什么"实现"是独立课题

第三篇(SCD)讲了 Type 2 是什么、为什么;这一篇讲在引擎上怎么落地。核心约束来自 Hive/MaxCompute 时代:没有(或昂贵)行级 UPDATE/DELETE,所以"关链 + 开链"这两次 UPDATE 要换成"一次全量重写"——把要写的所有行(关链的、开链的、保持的)算成一个完整结果集,INSERT OVERWRITE 覆盖整表(或分区)。拉链表实现本质是在不可变表上模拟 UPDATE 的套路。现代引擎(Doris/StarRocks/Spark 3/Postgres)有 UPDATE 和 MERGE INTO,写法更短,但三动作语义完全相同。

2. 标准实现:当日增量 × 开链行 比对(完整 SQL)

表结构沿用第三篇的 dim_dealer(代理键 dealer_sk、自然键 dealer_id、SCD2 白名单字段、start_date/end_date/is_current/version)。核心作业一步到位:

-- dim_dealer 拉链加工(Hive 方言,一次 INSERT OVERWRITE 全量重写)
WITH src AS ( -- ① 当日增量,row_number 判"新":一天多变取末状态
SELECT dealer_id, dealer_name, grade, region, status, settle_account
FROM (
SELECT *,
row_number() OVER (PARTITION BY dealer_id
ORDER BY op_ts DESC) AS rn
FROM ods_dms_dealer_inc
WHERE ds = '${today}'
) t WHERE rn = 1 -- 拉链粒度 = 天
),
cur AS ( -- ② 现有开链行:只取开链!(忘掉这行 = 链爆炸)
SELECT * FROM dim_dealer
WHERE end_date = '9999-12-31'
),
cmp AS ( -- ③ 比对打标:N 新增 / U 变化 / S 不变 / D 消失
SELECT
coalesce(s.dealer_id, c.dealer_id) AS dealer_id,
s.dealer_name, s.grade, s.region, s.status, s.settle_account,
c.dealer_sk AS old_sk,
c.dealer_name AS old_name, c.grade AS old_grade,
c.region AS old_region, c.status AS old_status,
c.settle_account AS old_account,
c.start_date AS old_start,
c.version AS old_ver,
CASE
WHEN c.dealer_id IS NULL THEN 'N' -- 新增 → 开链
WHEN s.dealer_id IS NULL THEN 'D' -- 消失 → 关链+软删(全量比对才有)
WHEN nvl(s.grade, '') <> nvl(c.grade, '') -- NULL-safe!
OR nvl(s.region, '') <> nvl(c.region, '')
OR nvl(s.status, '') <> nvl(c.status, '')
OR nvl(s.dealer_name, '') <> nvl(c.dealer_name, '')
OR nvl(s.settle_account, '') <> nvl(c.settle_account, '')
THEN 'U' -- 变化 → 关链 + 开链
ELSE 'S' -- 不变 → 保持
END AS flag
FROM src s
FULL OUTER JOIN cur c ON s.dealer_id = c.dealer_id
),
written AS (
SELECT -- ④a 关链:旧行收口到昨日
old_sk AS dealer_sk, dealer_id,
old_name, old_grade, old_region, old_status, old_account,
old_start AS start_date,
'${yesterday}' AS end_date,
0 AS is_current,
old_ver AS version
FROM cmp WHERE flag IN ('U','D')
UNION ALL
SELECT -- ④b 开链:新增 + 变化的新状态
hash(dealer_id, CASE WHEN flag='N' THEN 1 ELSE old_ver+1 END) AS dealer_sk,
dealer_id, dealer_name, grade, region, status, settle_account,
'${today}' AS start_date,
'9999-12-31' AS end_date,
1 AS is_current,
CASE flag WHEN 'N' THEN 1 ELSE old_ver + 1 END AS version
FROM cmp WHERE flag IN ('N','U')
UNION ALL
SELECT -- ④c 保持:不变的原样保留
old_sk, dealer_id,
old_name, old_grade, old_region, old_status, old_account,
old_start, '9999-12-31', 1, old_ver
FROM cmp WHERE flag = 'S'
)
INSERT OVERWRITE TABLE dim_dealer -- ⑤ 全量重写 = 天然幂等
SELECT dealer_sk, dealer_id, dealer_name, grade, region, status,
settle_account, start_date, end_date, is_current, version
FROM written;

逐条对应总监口径:FULL OUTER JOIN 在 ③;新增/变化/不变三动作在 ④a–④c;row_number 判新旧在 ①(还有一处用途见第 4 节);哨兵开链在 ② 和 ④b。

两个简化变体:源只有全量快照(无增量流水)时,src 直接取全量、去掉 row_number,同时 'D' 动作开始生效;字段多时把白名单字段拼串存 attr_md5 冗余列,比对退化成一次字符串比较(省时省心,代价是列变更要同步刷 md5 定义)。

3. 加工数据流

flowchart TD
SRC[当日增量流水] --> DEDUP[row_number 日末去重]
DEDUP --> J[FULL OUTER JOIN]
CUR[现有开链行 end_date 哨兵] --> J
J --> FLAG[打标 N 新增 U 变化 S 不变 D 消失]
FLAG --> A[关链 旧行收口到昨日]
FLAG --> B[开链 新行 start 今日 end 哨兵]
FLAG --> C[保持 原样保留]
A --> OUT[INSERT OVERWRITE 全量重写]
B --> OUT
C --> OUT
OUT --> CHK[校验 断链 重叠 is_current 唯一]

4. row_number 的第二处用法:写出幂等

重跑是拉链作业的常态(上游补数、失败重试)。INSERT OVERWRITE 全量重写天然幂等的前提是输入确定、写出无重复——当两个输入源可能给出同一自然键的多条候选(手工修正表 + 日常增量、重跑窗口重叠)时,在最终 SELECT 上再压一层:

row_number() OVER (PARTITION BY dealer_id, start_date
ORDER BY 批次优先级 DESC) = 1

每个(自然键, 版本起点)只保留优先级最高的一条。没有这层的增量追加式实现,重跑一次翻一倍——这就是"追加当 OVERWRITE"反模式的代价。

5. 实现路线对比

路线
输入
优点
代价/风险

增量流水比对(本篇主线)
当日增量 + 开链行
扫描量小
增量乱序/重复要治理;看不到"消失"

全量快照比对
每日全量快照 + 开链行
简单可靠、能发现消失
每日全表扫描;快照质量决定一切

MERGE INTO(现代引擎)
增量/全量
单语句 upsert,可走行级 UPDATE
关链+开链仍是两条语句;语义同三动作

dbt snapshot
源引用 + check/timestamp 策略
SCD2 全自动(valid_from/to、dbt_scd_id)
策略字段选择即白名单,仍要人工定

Flink CDC 实时拉链
changelog
秒级时效
链的乱序回退处理复杂

底层全是同一个语义:差异 × 三动作。会手写本篇 SQL,任何框架都是语法糖。

6. 工程细节与坑(按杀伤力排序)

join 忘开链过滤:历史版本全部参与比对 → 全表判"变化" → 链一夜膨胀百倍。

NULL 比较漏变化:NULL <> x 得 NULL 落进 ELSE"不变"——变化静默丢失,BI 数字悄悄错。全部白名单字段 nvl() 包裹。

技术字段进比对:etl_time、加载批次号进了白名单 → 每天都"变化" → 每天全量开链。白名单必须显式列业务字段。

关链日期与区间约定打架:关链 end_date='${yesterday}' 配开链 start_date='${today}' 是"end 不含当日"的左闭右开约定;若约定闭区间则关链应写 yesterday 且 join 用 f.date <= end_date。全仓只允许一种约定(呼应第三篇)。

消失键物理删除:源删掉的经销商直接不写 → 历史链凭空少一段。正确做法是关链 + status='deleted' 软删标记。

代理键失控:运行时用 hash(自然键+日期) 拼代理键,事实回键没有稳定映射。代理键生成规则归 DIM 层统一(生成器表或 hash(自然键,version))。

无校验裸奔:断链/重叠/is_current 唯一三条校验 SQL 是作业最后一步,缺一天迟早审计上门。

校验 SQL 示例(三条,DIM 层 DQC):

-- ① 断链/重叠:下一版本起点应等于当前终点+1 天
SELECT dealer_id FROM (
SELECT dealer_id, start_date,
lead(start_date) OVER (PARTITION BY dealer_id ORDER BY start_date) AS nxt
FROM dim_dealer) t
WHERE nxt IS NOT NULL AND nxt <> date_add(start_date, 1);

-- ② is_current 唯一且 end_date 为哨兵
SELECT dealer_id FROM dim_dealer
WHERE is_current = 1
GROUP BY dealer_id HAVING count(*) > 1
OR max(end_date) <> '9999-12-31';

-- ③ 变更数核对:今日关链数+新增开链数 = 源变更数

7. 现代引擎速写

StarRocks/Doris:主键模型只有"当前态",历史链仍要版本化行设计(主键 = dealer_id + version),或者当前态放 OLAP、历史链归湖/分层篇的 silver 层——两层各管一头。

Spark 3 / Postgres MERGE INTO:MERGE INTO cur USING src 命中且不同 → UPDATE 关链;随后第二条语句 INSERT 新链行。比 Hive 短,语义不变。

dbt snapshot:{% raw %}{% snapshot %}{% endraw %} + check_strategy(白名单字段列表)一行配置生成完整 SCD2,元数据列 dbt_valid_from/to 即 start/end_date——本篇全流程的自动化封装,白名单仍是你定的。

8. 渠道场景落地(承接系列)

调度位置(分层篇):DIM 层作业,排在 ODS 就绪之后、DWD 事实回键之前——ods_dms_dealer_inc → dim_dealer 拉链 → 事实表回键(late-arriving 补键)→ DWD。

渠道特有项:经销商手工修正频繁 → 增量之外保留一条"手工修正"优先级更高的源,row_number 排序时置顶;合作终止(status='terminated')照常拉链,end_date 收口在终止日,审计复算天然正确(第三篇的 Q1 返利复算场景)。

作业三校验 + 变更数核对接告警;月度跑一次全链 health check(断链/重叠全表扫描)。

9. 总监口径逐句评审

原话
评审

"当日增量与现有开链数据 full outer join"
✅ 标准主线。补两个前提:只 join 开链行(忘过滤=链爆炸);增量流水先 row_number 日末去重

"新增 → 开链;变化 → 旧记录关链 + 新记录开链;不变 → 保持"
✅ 三动作正确。补第四动作:消失 → 关链 + 软删(全量比对场景必答)

"row_number() 判新旧"
✅ 两处标准用法:增量日末取末状态(拉链粒度=天)+ 写出幂等每键选优

"end_date = '9999-12-31' 表示开链"
✅ 推荐哨兵日期(NULL 当开链标记伤 join/索引)。补:哨兵只是表层的"区间约定"——闭/开区间必须全仓统一,这才是事故源

(缺失)NULL-safe 比较
Hive 里 NULL <> x 得 NULL,变化静默判"不变"——白名单字段逐个 nvl

(缺失)幂等
INSERT OVERWRITE 天然幂等;追加式实现重跑翻倍

(缺失)校验
断链/重叠/is_current 唯一三件套是作业最后一步

10. 反模式清单

join 不过滤开链行——一夜链爆炸。

裸比较不带 nvl——变化静默漏判。

技术字段进白名单——每天全量开链。

关链 end_date 用 today——与开链 start_date 重叠一天,区间约定崩。

追加式写入当全量重写——重跑翻倍。

代理键运行时拼——事实回键失控。

无校验裸奔——断链半年无人知。

消失键物理删除——审计链断一段。

11. 参考资料

尚硅谷电商数仓课程讲义(全量快照比对 + row_number 判新旧模式的流行源头,本篇主线之社区出处)

Kimball, Ross.《The Data Warehouse Toolkit》3rd ed., 2013,第 5 章(SCD 之 ETL 部分);《The Kimball Group Reader》ETL 章节

阿里巴巴.《大数据之路》2017(拉链表工程实践与 DIM 层治理语境)

dbt Labs 文档:Snapshots(check/timestamp 策略,SCD2 自动化)

本系列前篇:《缓慢变化维SCD》(Type 2 理论与区间约定)、《数仓分层设计》(DIM 层调度位置)、《事实表三种类型》(事实侧时间语义)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:拉链表实现_深度研究_2026-09-08.md

#拉链表 #SCD #Hive #维度建模 #数据仓库 #渠道数据 #智柴

暂无表态
✨
文本版 · 供搜索与朗读

雪花模型 vs 星型 深度研究

数据仓库 · 维度建模 · 深度研究

雪花模型 vs 星型 深度研究

雪花模型 vs 星型?星型:维度反规范化、join 少、空间换时间;雪花:维度再拆 3NF,节省空间但 join 多。报表场景默认星型。

目 录

一页结论
定义与图形
逐点对比
雪花的三个真实理由(什么时候拆)
星型的纪律:拍平不是躺平
宽表与星系:两个近邻
渠道场景(承接系列)
总监口径逐句评审
反模式清单
参考资料

0. 一页结论

两者的差别只有一个位置:维度表要不要继续规范化。事实表两边完全一样;星型把维度拍平成一张宽维表,雪花把维度再拆出父级维(商品维 → 品类维)。Kimball 给拆出来的父维起了专门的名字:支架维(outrigger)。

"节省空间"这个雪花红利在 2026 年已经失效。维度表本来只占数仓存储的百分之一以下——商品维百万行再冗余品类名,省的是 KB 级;而多一跳 join 的代价(引擎、优化器、BI 语义、可读性)是实打实的。所以"空间换时间"这笔账对星型是一边倒。

雪花的真实存在理由是三个窄场景:① 支架维被多个维度共享(品牌维同时挂在商品维和经销商维下);② 维度父级高频重排(品类树重组,拆开后商品维不被牵动);③ 老 OLAP 引擎的层级钻取与存储极致受限的嵌入式环境。三个都不叫"节省空间"。

"报表场景默认星型"一锤定音 ✅。join 一跳、语义一读即懂、BI 工具默认优化、LLM/ChatBI 时代 join 路径显式化更是决定性加分(第一篇语义层论证在此落地)。

两个必须分清的近邻:宽表是星型的下游预物化(性能层手段,不是建模替代);星系/星座(多事实表共享维度)是总线矩阵的物理形态,跟雪花是两回事。雪花化回潮(维度越拆越多、join 越写越长)是建模失控的第一信号。

1. 定义与图形

星型:事实表居中,维度环绕,任何分析一跳 join 到位。

flowchart LR
DD[dim_dealer] --- F((fact_channel_sales))
DP[dim_product] --- F
DS[dim_store] --- F
DT[dim_date] --- F

雪花:维度内部再规范化,维度彼此挂接,多跳 join。

flowchart LR
DD[dim_dealer] --- F((fact_channel_sales))
DP[dim_product] --- F
DS[dim_store] --- F
DT[dim_date] --- F
DP --- DC[dim_category]
DC --- DB[dim_brand]
DS --- DR[dim_region]

历史注脚:雪花是"规范化学派"在维度建模内部的回声——90 年代论战时 Inmon 阵营主张维度也该 3NF(雪花),Kimball 在 Toolkit 里专门写章节反对("snowflakes should be avoided"),只给支架维留了受控的口子。今天说"雪花"其实指两个不同的东西:维度建模内部的支架维(本篇主角,局部手段)和 OLAP 时代的全局雪花化(已被淘汰)。

2. 逐点对比

维度
星型
雪花

join 数
事实 → 每维度一跳
事实 → 维度 → 父维度两到三跳

空间
维度描述冗余存储
父级只存一份

查询性能
少 join,引擎友好
每跳都是代价

语义可读性
业务人员直接读
要理解支架层级

维度维护
改宽维表一行
改规范化父表一处

一致性来源
一致性维度(纪律背书)
父表共享(结构背书)

层级钻取
层级路径列内置
天然层级

LLM/ChatBI
join 路径显式
多跳 join 易幻觉

空间账:100 万行的商品维冗余一个 1 万行的品类名,多花的存储以 MB 计;换取的是每次品类查询少一跳 join。存储按 2026 年的价格可以忽略,join 的延迟和复杂度不能。

性能账:现代 MPP(StarRocks/Doris)join 不弱,星型全场 4–6 个 join 毫无压力;雪花把 join 数翻倍仍能跑,但 BI 生成 SQL 的复杂度、优化器的工作量、出错面全部随跳数增长。ClickHouse 类 join 弱的引擎则连星型都拍宽表——那是物理加速选择,不是建模选择(呼应分层篇)。

3. 雪花的三个真实理由(什么时候拆)

支架维被多个维度共享:渠道里品牌维同时挂在商品维(商品属于品牌)和经销商维(经销商代理品牌)之下。拆出共享一份 vs 各自冗余两份——冗余版会在品牌更名时出现两处不一致。这是 Kimball 认可支架维的头号场景。

父级高频重排:品类树年年重组。拆开后品类走自己的 SCD2,商品维不跟着拉链;拍平版每次品类调整都要给百万行商品维刷 Type 2。

老 OLAP 层级钻取 / 极致存储受限:SSAS 多维时代的天然层级、嵌入式设备里的迷你仓——2026 年的新项目基本碰不到了。

4. 星型的纪律:拍平不是躺平

冗余的是描述性文本,不是事实:维度表仍是"一实体一版本一行"(SCD 管着),拍平只把父级的名字冗余进行里。

层级路径要内置:拍平的代价是上卷路径变成列(category_id / category_name / category_l2_name / category_l1_name),层级变化走维度 SCD2,不是刷历史。

一致性维度仍是承重墙:星型防孤岛靠"同一维度全仓一份物理表"(第一篇总线矩阵),雪花用父表共享背书一致性——两条路,星型那条靠纪律。

雪花化回潮的信号:维度表数量持续上涨、BI 用户开始写三表 join、"这个属性挂哪张维表"成为讨论话题。出现即收敛:要么拍平,要么明确支架维身份。

5. 宽表与星系:两个近邻

大宽表是星型的下游,不是对手:明细层星型管口径,汇总/加速层宽表管性能(分层篇已立此纪律)。把宽表当建模是倒果为因。

星系/星座(constellation):多个事实表共享一组一致性维度——这是 Kimball 总线架构的物理形态,事实表之间不直接 join,靠共享维度对齐。它和雪花无关联:星座管"事实之间",雪花管"维度内部"。渠道数仓天然是星座:进、销、存、费用多事实共享 dim_dealer/dim_product/dim_date。

6. 渠道场景(承接系列)

渠道维度天然带层级:商品(条码→SKU→品牌→品类)、经销商(→大区→集团)、终端(→业态→城市)。建议形态:星型为主 + 品牌支架维的半雪花——

dim_product 拍平含品牌/品类路径列(星型主体,报表一跳到位);

dim_brand 作为支架维独立存在(被商品维与经销商代理关系共享,渠道对账要求"同一品牌全仓一个名字");

品类树另走独立 SCD(重排频繁,牵动面控制在父维)。

同一条查询的对比——"2025 年华东区洗护品类销售额":星型 fact_channel_sales ⋈ dim_product ⋈ dim_dealer ⋈ dim_date(3 跳、3 表);全局雪花再加 dim_category ⋈ dim_brand(5 跳)。BI 用户和 LLM 都会感谢前者。

7. 总监口径逐句评审

原话
评审

"星型:维度反规范化、join 少、空间换时间"
✅ 但"空间换时间"是历史表述——存储红利按 2026 年价格可忽略,星型真正的赢面是 join 少 + 语义显式(BI 与 LLM 双友好)

"雪花:维度再拆 3NF,节省空间但 join 多"
✅ 补:雪花在维度建模内部的正身是支架维(局部受控手段);全局雪花化已被淘汰

"报表场景默认星型"
✅ 一锤定音。补三例外:支架维共享、父级高频重排、老 OLAP/嵌入式

(缺失)宽表关系
宽表是星型的下游物化,明细层仍应星型

(缺失)星系/星座
多事实共享维度 ≠ 雪花;它是总线矩阵的物理形态

(缺失)回潮警示
维度表数量上涨、join 变长 = 建模失控第一信号

8. 反模式清单

为省空间雪花化:省下 KB,赔上每一跳 join。

支架维泛滥:一张维表挂出 20 个支架,星型退化成雪花网。

事实表互相关联:跨过程分析该靠一致性维度对齐(星座),不是事实 join 事实。

宽表当建模:拍平进物理加速层,别拍进明细层。

层级拍平不走 SCD:品类重排刷历史,报表全面漂移。

把 OLAP 引擎弱 join 当建模理由:ClickHouse 拍宽表是加速手段,明细层口径还在星型里。

9. 参考资料

Kimball, Ross.《The Data Warehouse Toolkit》3rd ed., Wiley, 2013(反对雪花化的权威章节、支架维 outrigger 的定义与使用边界)

kimballgroup.com Design Tips(snowflake vs outrigger 相关设计技巧)

本系列前篇:《Kimball维度建模_vs_Inmon范式建模》(维度工具箱、总线矩阵、语义层与 LLM)、《数仓分层设计》(宽表位置)、《事实表三种类型》(事实侧,与本篇维度侧互补)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:雪花模型vs星型_深度研究_2026-09-08.md

#星型模型 #雪花模型 #维度建模 #支架维 #数据仓库 #渠道数据 #智柴

暂无表态
✨
文本版 · 供搜索与朗读

数据湖与湖仓一体 深度研究

数据仓库 · 湖仓架构 · 深度研究

数据湖与湖仓一体 深度研究

数据湖 = 对象存储上的开放格式原始数据池,schema-on-read;湖仓一体(Lakehouse)= 在湖上加表格式,获得 ACID、schema evolution、time travel,兼具湖的便宜灵活和仓的事务可靠。

目 录

一页结论
数据湖:定义、由来与双面性
湖的五宗罪与表格式的补课清单
2026 格式格局
与系列概念的关系(正交性)
渠道场景落地
总监口径逐句评审
选型与反模式
参考资料

0. 一页结论

总监口径精准,且抓住了要害:湖与仓的差别不在存储介质,在表契约。湖没有写入时的格式约束(schema-on-read,读时才解释),仓的可靠性来自 schema-on-write + 事务;湖仓一体的本质是把"表"这层契约下放到对象存储上——表格式三件套(ACID、schema evolution、time travel)补齐了湖欠仓的课。

理解湖仓要先理解湖的五宗罪(数据湖沦为数据沼泽 data swamp 的机理):无事务(并发写互相覆盖)、无 schema 演进(加列=全量重写)、无行级更新(Hive 拉链表为什么诞生的原因)、无时间旅行(覆盖即丢失)、无 catalog(目录里是什么靠文档和信仰)。表格式就是逐条补这五课。

2026 格式格局已收敛:Iceberg 是跨供应商的事实标准(v3 规格 2025 年中定稿:deletion vectors、VARIANT 类型、row lineage;Databricks/Snowflake 先后落地);Hudi 退守高频 upsert/CDC 场景;Paimon 绑定 Flink 做实时湖仓、国内增长最快;Delta 留在 Databricks 生态(UniForm 兼容 Iceberg 读取)。战场已从"格式之争"上移到 catalog 之争(Unity/Polaris/Nessie/S3 Tables/Glue)。

湖仓不替代系列里的任何一层:湖仓解决"存储载体与表契约",分层管"加工阶段"(bronze/silver/gold ≈ ODS/DWD+DIM/ADS),星型仍管"消费语义"。三者正交,gold 层照旧是 Kimball。

渠道场景的标准姿势:冷明细归湖(多年历史,Iceberg 按月分区),热层进 OLAP 加速(StarRocks/Doris);time travel 给渠道审计加了"表级时光机"——但它是工程兜底,维度历史的正解仍是 SCD 拉链(表级版本 ≠ 属性级版本)。

1. 数据湖:定义、由来与双面性

定义:对象存储(S3/OSS/COS/HDFS)上的开放格式(Parquet/ORC/JSON/CSV)数据池,任何引擎可读,写入不校验。

由来:2010 年代对数据仓库两件事的反叛——贵(仓储许可证+ tightly coupled 存储/计算)和装不下(日志、文本、图像)。"先把数据都扔进湖里,要分析时再解释"是当时的解放。

四大吸引力:便宜(对象存储按 GB 月计)、灵活(任意格式任意 schema)、解耦(存储计算分离)、全类型(结构化到非结构化)。

schema-on-read 的双面性:写入零约束换来了摄取速度,代价是口径解释权下放给每个读者——A 团队读某个 JSON 字段是字符串日期,B 团队当枚举。这与本系列一贯的"口径要收敛不要旁落"正面冲突:湖可以容脏,但口径纪律不能省。

2. 湖的五宗罪与表格式的补课清单

湖的原罪
后果
表格式怎么补

无事务
并发写互相覆盖;读者看到半成品
快照原子提交(乐观并发控制),读者只见完整快照

无 schema 演进
加列/改列名 = 全量重写
元数据层管列映射,演进零重写

无行级更新/删除
只能覆盖分区(拉链表被迫诞生)
merge-on-read / copy-on-write 行级 delete(v3 deletion vectors 加速)

无 time travel
覆盖即丢失,误删无法回滚
快照链,AS OF 查历史版本,顺带审计与回滚

无 catalog
目录语义靠文档
元数据清单 + 统一 catalog(格式之上再补一层)

表格式的工作原理(以 Iceberg 为例):数据文件之上加元数据树——manifest list → manifest → data file,每次提交生成新快照;ACID 是快照原子切换,schema evolution 是元数据里的列映射,time travel 是快照链,数据跳过靠 manifest 里的 min/max 统计,hidden partitioning 让分区声明跟列值走(写错分区过滤不再翻车)。

附赠能力:小文件 compaction、行级 upsert(MERGE INTO)、统计信息裁剪、row lineage(v3,行级血统)。这已经不是"给湖打补丁",是一套完整的表引擎。

3. 2026 格式格局

格式
起源
强项
2026 状态

Iceberg
Netflix → Apache
中立、多引擎、hidden partitioning、生态最广
事实标准:v3 规格落地(deletion vectors/VARIANT/row lineage),Databricks 公开预览、Snowflake 2026-05 GA,国内大厂主流

Delta Lake
Databricks
与 Databricks/Spark 深度一体、UniForm 双读
留在 Databricks 生态,靠 UniForm 兼容 Iceberg 读取

Hudi
Uber
高频 upsert/CDC、小文件治理
退守实时写入重的场景,国内金融/互联网有存量

Paimon
Flink 社区(阿里贡献)
流式湖仓、与 Flink 原生集成、部分更新
国内实时数仓增长最快,Flink 系首选

国内实践共识:离线多引擎分析选 Iceberg;Flink 实时链路选 Paimon;强 CDC upsert 老场景 Hudi 仍成熟;越来越多团队用 "Paimon/Hudi 做实时明细层 + Iceberg 做分析服务层" 的组合。Databricks 全家桶用户留 Delta。

格式之上的新战场——catalog:Unity Catalog(Databricks)、Apache Polaris(Snowflake 捐赠)、Nessie(git 式分支)、AWS S3 Tables(托管 Iceberg)、Glue。格式解决了"表"的问题,catalog 解决"谁的第几版表"——权限、血缘、跨引擎发现都在这层。开放格式的开放性正从引擎无关滑向"catalog 锁定",选型时 catalog 的中立性比格式更值得计较。

4. 与系列概念的关系(正交性)

与分层:湖仓是载体,分层是结构。medallion(bronze/silver/gold)≈ ODS/DWD+DIM/ADS(分层篇 5.4 合流图原样成立)。湖仓不改变"禁绕层、禁回环"的纪律。

与维度建模:gold 层照旧 Kimball 星型 + 一致性维度;语义层与 LLM 论证不变。湖仓给出更宽的表与更便宜的存储,不给出口径。

与拉链表(关键辨析):time travel 是表级时光机("这张表上个月长什么样"),SCD 拉链是属性级历史("经销商当时是什么等级")。事实表历史可以靠 time travel 兜底,维度历史仍要 SCD——渠道审计两者都要,别用 time travel 替代拉链。

与 Inmon/Kimball 之争:湖仓 silver 层集成(DV/3NF 可选)+ gold 层星型消费——两派在湖仓架构里各得其所,历史争论以"分层共存"收尾(第一篇结论在存储层的重演)。

5. 渠道场景落地

冷热分层:5 年明细归湖(Iceberg,按月分区,Parquet),近 90 天热数据进 StarRocks/Doris 做指标加速;对象存储存冷数据成本约为 OLAP 副本的十分之一。

多源主数据归一:DMS/ERP/电商/费控的经销商编码交叉表在湖上跑批量(OneID),产出喂 DIM 层——湖的"任意格式全量收"在这里发挥到极致。

审计双保险:"复算 2025-03 的库存快照口径" = 拉链维(属性历史)+ Iceberg time travel(表级快照)各答一半。

实时链路:POS 小票 Flink → Paimon(分钟级可查),窗口聚合出准实时 DWS,T+1 由 Iceberg 层接管权威口径。

治理义务:湖上的原始池也要 catalog + 血缘 + 保留策略——"原始数据池"不是"免治理池",沼泽就是治理缺位的湖。

6. 总监口径逐句评审

原话
评审

"数据湖 = 对象存储上的开放格式原始数据池,schema-on-read"
✅ 精准。补:schema-on-read 的代价是口径解释权下放,湖可以容脏、口径纪律不能省;没有治理的湖的终局是沼泽

"湖仓一体 = 在湖上加表格式,获得 ACID、schema evolution、time travel"
✅ 抓住要害。补:还有行级更新、hidden partitioning、compaction;"表格式之上是 catalog"——2026 年真正的锁定点

(缺失)格式选型
Iceberg 默认(中立+生态);Flink 实时 Paimon;Databricks 系 Delta;强 upsert 存量 Hudi;国内常见"实时层 Paimon/Hudi + 分析层 Iceberg"组合

(缺失)与分层/建模的关系
湖仓是载体不是方法论:分层管加工阶段、星型管消费语义,全部照旧

(缺失)time travel ≠ 拉链
表级版本 ≠ 属性级版本,维度历史仍是 SCD 的活

7. 选型与反模式

选型速查:新建通用湖仓 → Iceberg;Databricks 全家桶 → Delta;Flink 重度实时 → Paimon;高频 CDC upsert 存量 → Hudi;多格式并存 → 统一 catalog 收口(别让每个格式自带一套目录)。

反模式清单:

湖直接喂 BI——没有 silver/gold 加工,口径漂移回到史前。

无 catalog 裸奔——"这个目录是什么"靠口口相传。

小文件不治理——compaction 缺位,对象存储列出元数据就先超时。

schema-on-read 当口径管理——每个读者自己解释字段,分歧在半年后爆发。

time travel 当无限保留——快照不过期,元数据与存储双爆炸;要定 expiration 策略。

用 time travel 替代 SCD——表级版本答不了"当时等级"。

多格式自选无 catalog 收口——开放格式的开放性被 catalog 锁定吞掉。

8. 参考资料

Apache Iceberg 官方规格(iceberg.apache.org/spec)与 v3 变更(deletion vectors、VARIANT、row lineage;规格 2025 年中定稿,1.10/1.11 发布线交付)

Databricks 博客:Iceberg v3 公开预览;Snowflake 2026-05 GA
-袋鼠云《湖仓一体架构设计:Iceberg Hudi Paimon 对比与选型》;腾讯云《数据湖技术选型指南》;知乎专家圆桌(国内选型共识:Hudi 高写入、Paimon 实时、Iceberg 中立)

Armbrust 等.《Lakehouse: A New Generation of Open Platforms that Unify Data Warehousing and Advanced Analytics》, CIDR 2021(lakehouse 概念的学术出处)

本系列前篇:《数仓分层设计》(medallion 与 ODS/DWD/ADS 映射)、《缓慢变化维SCD》《拉链表实现》(属性级历史的正解)、《Kimball维度建模vs Inmon范式建模》(gold 层与语义层)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:数据湖与湖仓一体_深度研究_2026-09-08.md

#数据湖 #湖仓一体 #Iceberg #Paimon #DeltaLake #表格式 #智柴

暂无表态
✨
文本版 · 供搜索与朗读

StarRocks MPP OLAP 引擎 深度研究

数据仓库 · OLAP 引擎 · 深度研究

StarRocks MPP OLAP 引擎 深度研究

新一代 MPP OLAP 引擎(与 Apache Doris 同源),向量化 + CBO + Pipeline 执行引擎,强项是高并发报表、多表 join、实时 upsert、湖上查询——正好是渠道报表洞察的画像。含:架构(FE/BE/CN、3.x 存算分离)、四种表模型、物化视图、Colocate Join、索引、分区分桶、vs ClickHouse、vs Doris、导入、查询调优。

目 录

一页结论
血统与定位
架构:FE / BE / CN
四种表模型(必考重点)
加速手段全家桶
分区分桶
vs ClickHouse
vs Doris
导入与实时链路
查询调优方法论:看 profile 四查
渠道场景完整画像(承接系列)
总监口径逐句评审
反模式清单
参考资料

0. 一页结论

总监画像成立且选型对靶:渠道报表 = 多表 join(星型)+ 高并发(报表/看板多人)+ 实时 upsert(库存最新态)+ 湖上查询(冷明细在 Iceberg)——这四项正好是 StarRocks 相对 ClickHouse 的四连胜区。向量化 + CBO + Pipeline 是它在多表 join 上不虚的引擎底座。

血统要说准:同源 = 2020 年百度 Doris 团队部分核心成员 fork Doris 0.13 之前的早期版本做闭源 DorisDB,2021-09 开源更名 StarRocks(Apache 2.0),商业公司 CelerData/镜舟。此后两项目底层已完全分叉(优化器、存储引擎、表模型各自重写),今天的"同源"只剩历史意义,不存在兼容承诺。

四种表模型的记忆锚:Duplicate 留原始(ODS/审计底表)、Aggregate 预聚合(预汇总提速)、Unique 老更新模型(MoR 读时合并慢,2.x 也支持 MoW)、PK 主键模型(delete-and-insert,实时 upsert 首选,代价是主键索引常驻内存)。总监一句话选型完全正确。

3.x 存算分离补一个 2026 更新:CN 无状态 + 数据单副本在对象存储的架构 3.0 落地;4.0(2025-10)的 File Bundling 把对象存储 API 调用降约 90%,官方定位为补掉存算分离在实时场景的"最后瓶颈"(小文件写放大)。生产案例存储成本降约 90%、计算弹性省约 30%(京东物流)。

与系列的正交关系:引擎解决"怎么算",分层解决"加工到哪一步",星型解决"口径长什么样"。渠道数仓的标准拓扑:明细/维表 Duplicate+PK 在 StarRocks 作热层,5 年冷明细在 Iceberg 湖上(external catalog 直查),聚合提速靠聚合表 + 异步物化视图——引擎选择不豁免建模纪律。

1. 血统与定位

时间
事件

2013 前
Google Mesa 论文(交互级聚合数据存储)启发 Doris 谱系

2008–2018
百度内部 Palo → 开源贡献 Apache(Incubator 2018),成为 Apache Doris

2020-02
百度 Doris 团队部分核心成员离职创业,fork Doris 0.13 之前的早期版本,做闭源商业产品 DorisDB

2021-09
商标原因更名 StarRocks,宣布开源(Apache 2.0);商业公司 CelerData(国内品牌镜舟)

2022–2024
向量化、CBO、Pipeline 执行引擎成熟;2.4+ 异步物化视图;3.0 存算分离(shared-data)

2025-10
4.0 发布:File Bundling、JSON 一等类型、DECIMAL256、ASOF JOIN、Iceberg compaction 深化

"同源"的准确含义:共同祖先在 2020 年之前的 Doris 早期代码;此后两边各自重写了优化器、存储引擎与表模型体系。今天两者更像"同一设计哲学的两条独立演化线"(MPP、FE/BE、类 Mesa 表模型),而不是一个生态的两个发行版。

2. 架构:FE / BE / CN

flowchart LR
CLI[SQL 客户端 / BI / LLM] --> FE
subgraph FE[FE 集群 元数据+规划]
L[Leader 主] -- BDBJE 复制 --> F1[Follower x2 备+选主]
F1 --> O1[Observer 只读扩展]
end
FE -->|计划分片| BE1[BE / CN 1]
FE --> BE2[BE / CN 2]
FE --> BE3[BE / CN 3]
subgraph shared_data[3.x 存算分离模式]
BE1 --> OS[对象存储 S3 OSS COS<br>数据单副本]
BE2 --> OS
BE3 --> OS
end

FE(Frontend):元数据管理、SQL 解析、规划、调度。多 Follower 通过 BDBJE(BerkeleyDB Java Edition)复制元数据实现 HA,Leader 由多数派选出;Observer 不参与选主、只扩展读。客户端连任一 FE。

BE(Backend):存算一体模式的数据+计算节点,tablet 的存储与副本管理。

CN(Compute Node):3.x 存算分离模式下替代 BE——无状态纯计算,全量数据以单副本放对象存储(S3/OSS/COS/HDFS),本地盘只做 Data Cache(缓存命中时性能接近存算一体);底层由 StarOS 抽象层调度。扩缩容无数据迁移,秒级弹性。

4.0 的补刀:File Bundling 把小写入捆绑成大文件,对象存储 API 调用降约 90%、消除写放大——存算分离从"离线友好"走向"实时也扛得住"。

3. 四种表模型(必考重点)

模型
机制
场景
渠道例子
代价/注意

Duplicate 明细
按 key 排序存储,append 不去重
原始明细、审计底表
进销存流水、POS 小票(ODS/DWD 落地)
排序键即前缀索引,要按查询模式设计

Aggregate 聚合
按 key 预聚合,指标列配 sum/max/replace/hll_union/bitmap
预汇总提速
经销商×日销量表、UV 表
明细丢失,改口径要重导;只能用模型支持的聚合函数

Unique 更新
key 相同新覆盖旧;旧实现 merge-on-read(读时合并多版本,谓词无法下推,查询慢);2.x+ 支持 merge-on-write
写多读少的更新场景(存量老表)
老库存表
MoR 查询性能差,新表别再选它

Primary Key 主键
全新存储引擎(1.19 引入):主键持久化索引 + delete-and-insert,写入即去重
实时 upsert 首选
库存最新状态表、主数据宽表
主键索引常驻内存——大表主键要短(用整型代理键,别用长字符串拼键)

与 Doris 的术语映射(同源两家的分叉点,面试常考):Doris 是三模型(Duplicate/Aggregate/Unique),Unique 有 MoR 与 MoW 两种实现(MoW 1.2 引入、2.1 起默认启用,性能接近明细,官方口径典型提升可达 10 倍);StarRocks 把同类能力独立成第四种 PK 模型。Doris Unique(MoW) 与 StarRocks PK 目标一致(写时消解重复主键、查询免合并),底层实现完全不同。

选型决策树:要保留全部原始?→ Duplicate。按维度预聚合可接受?→ Aggregate。要实时更新且查询要快?→ PK(主键短)。历史遗留写多读少?→ Unique MoR(别新建)。

4. 加速手段全家桶

4.1 物化视图(两代)

同步 MV:单表、导入事务内刷新,改写对用户透明;限制在单表聚合(count/sum 等固定函数集)。

异步物化视图(2.4+):支持多表 join(含湖表)、定时/手动刷新、查询自动改写(CBO 判断改写是否更优)、支持分区级增量刷新(2.5+,只刷变化分区)。高频报表的核心加速手段——把"经销商×品类×日报"这类反复出现的 join+聚合定成一个异步 MV,BI 无感提速。

4.2 Join 优化

Colocate Join:两表按 join key 同分布 bucket(同 colocate group),join 免网络 shuffle。高频维度 join 提速利器;坑:副本分布被集群绑定(colocate 组的 tablet 修复/迁移受约束)、两表桶数必须一致、建组要在建表时规划好。

Bucket Shuffle Join:按 join key 分桶把 shuffle 下沉到单侧,代价小于 broadcast/shuffle。

Runtime Filter(顺带):join 探测端向扫描端注入过滤谓词,大表 join 小表时自动裁剪扫描量——profile 里的常客。

4.3 索引

索引
适用
渠道例子

前缀索引(排序键)
每表必设——把高频过滤列放排序键最前
流水表按 (dealer_id, ds) 排序

Bitmap 索引
低基数列等值过滤
业态、等级、状态

Bloomfilter
高基数列点查
单据号、终端 ID

BITMAP 精确去重
count distinct 提速一个量级
活跃门店数、拜访终端数(列类型 BITMAP,导入 to_bitmap)

5. 分区分桶

分区:按天/月切(事实表按天),查询分区裁剪 + 生命周期 TTL(到期自动删,呼应分层篇"ODS 只进不出撑爆仓"的解法)。

分桶:hash(key) 分布,决定并行度与 Colocate 能力。经验值:bucket 数 ≈ BE 数 × 单 BE 磁盘数的 1–2 倍量级;单 tablet 建议 1–10GB。桶过少并行不足(查询慢),过多则元数据与 compaction 压力。分桶键选高频过滤/join 键(dealer_id)。

6. vs ClickHouse

维度
ClickHouse
StarRocks

单表查询
极快(MergeTree + 物化视图预聚合的看家本领)
快,但单表极限略逊

多表 join
弱(join 语义与内存敏感,实践靠大宽表绕开)
强(CBO + Pipeline + Colocate,星型的主场)

并发
弱(高并发要靠物化/分布式协调层)
高并发报表原生

update/upsert
麻烦(Mutation 异步重分区)
PK 模型原生实时 upsert

湖上查询
有但非重点(S3 外表)
external catalog 直查 Iceberg/Hudi/Delta/Paimon,4.0 深化读写

生态心智
单表 BI/日志/行为分析
报表、湖仓、多模负载

结论同总监:渠道报表 = 多表 join + 高并发 + upsert + 湖查询 → StarRocks 靶心。CH 的正确用法是"单表行为分析/超大规模扫描"(用户行为漏斗、埋点),两边场景错开时可以共存(CH 做埋点,StarRocks 做报表)。

7. vs Doris

同源分家(第 1 节)。2026 年的差异面:StarRocks 在存算分离(3.x CN + 4.0 File Bundling)、异步物化视图(多表改写)、Pipeline 执行引擎上更激进;Doris 社区纯开源驱动、2.x MoW 默认后表模型能力靠拢。商业上 StarRocks 双轨(开源 + 镜舟企业版),Doris 依托社区与云厂商发行版。选型实话:两边都能胜任渠道报表;已有 Flink 实时栈、要湖上深水区(Iceberg compaction/写入)偏 StarRocks;社区支持与多云中立要求高、预算敏感偏 Doris。别按"谁功能多 5%"选,按团队生态与运维承接力选。

8. 导入与实时链路

通道
用法
渠道场景

Stream Load
HTTP 批量同步导入
经销商手工日结文件

Routine Load
Kafka 流式常驻消费
POS 小票流

Broker Load
湖/HDFS 批量
Iceberg/HDFS 历史回刷

Flink Connector
2PC exactly-once
实时链路主力(Flink → StarRocks 不重不丢)

PK + partial update
主键模型部分列更新
库存增量:只更新 on_hand_qty 列,不动其他列

9. 查询调优方法论:看 profile 四查

Scan:分区裁剪命中没有?前缀索引(排序键)命中没有?——不命中先改分区过滤条件/排序键。

Join:join 顺序合理吗(大表 build 小表 probe)?Colocate/Bucket Shuffle/Runtime Filter 生效没有?

改写:异步 MV 改写命中没有?没命中查 MV 的刷新状态与口径等价性。

并行:buckets 是否过少导致 instance 数不足(并行度低)?单 tablet 是否过大?

10. 渠道场景完整画像(承接系列)

拓扑:DWD 事实/维表在 StarRocks(Duplicate 流水 + PK 库存最新态);DWS 聚合表 + 高频报表异步 MV;5 年冷明细 Iceberg,external catalog 直查(湖仓篇的冷热分层在此落地);Flink → Routine/Flink Connector 2PC 进实时。

库存:PK 模型 + partial update 吃增量(dealer_id+product_id 短主键),版本快照事实仍按天进 Iceberg/聚合表——"最新态"(PK 表)与"按日历史"(快照表)是两张表,别混(事实表篇纪律)。

维表:DIM 层拉链维表进 StarRocks 后,BI 用"当前视图"(is_current=1)+ 需要历史口径时 join 版本键——SCD 纪律不因引擎改变。

报表提速组合拳:前缀索引(排序键=过滤列)→ 分区裁剪(按天)→ 聚合表(DWS 粒度)→ 异步 MV(多表 join 改写)→ Colocate(高频维度 join)。从便宜到贵依次上,profile 决定下一步。

11. 总监口径逐句评审

原话
评审

"向量化 + CBO + Pipeline"
✅ 引擎三件套准确

"强项:高并发报表、多表 join、实时 upsert、湖上查询"
✅ 四连胜区即 vs CH 的差异面,渠道画像对靶

"FE 多 Follower BDBJE 复制 HA,Observer 扩读"
✅ 准确;补:Leader 多数派选出,客户端连任一 FE

"3.x 存算分离:状态放对象存储,CN 无状态扩缩容"
✅ 3.0 引入;补 2026 更新:4.0(2025-10)File Bundling 把对象存储 API 调用降约 90%,实时场景的最后瓶颈被补

四种表模型 + 各自定位
✅ 全对;补:PK 代价=主键索引常驻内存(主键要短);Unique MoR 谓词无法下推,新表别选

"库存/主数据 PK 实时 upsert,明细 Duplicate,报表聚合表+MV"
✅ 一句话选型正确;补:最新态(PK)与按日历史(快照)是两张表

物化视图两代
✅ 同步单表/异步多表改写,准确

Colocate / Bucket Shuffle
✅ 补 Colocate 的坑:桶数一致 + 副本分布绑定

索引四件
✅ 补:前缀索引即排序键,建表时就要想好查询模式

bucket ≈ BE×磁盘 1–2 倍、tablet 1–10GB
✅ 经验值正确

vs CH / vs Doris
✅ 判断准确;补 Doris 侧事实:Unique MoW 2.1 起默认,两边底层已分叉

导入 / 调优四查
✅ 完整;partial update 配 PK 是库存增量的正解

12. 反模式清单

PK 模型主键用长字符串拼键——主键索引吃内存,大表直接撑爆;用整型代理键。

新表选 Unique MoR——读时合并慢且谓词无法下推,2026 年没有理由新建。

明细表选 Aggregate——明细丢了,改口径无法重算;聚合留给 DWS 粒度。

排序键随手设——前缀索引是每表第一个性能杠杆,应按高频过滤列设计。

桶数拍脑袋——过少并行不足,过多元数据/compaction 压力;按 BE×磁盘×1–2 估。

没有 TTL 的按天分区——存储只进不出(分层篇同款病)。

PK 最新态表当日志表查——"最新态"答不了"上月底库存",历史另建快照表。

湖上直查当常态——external catalog 查冷数据省钱,但高频报表该物化进内表/异步 MV。

13. 参考资料

StarRocks 官方文档:Primary Key Table、存算分离架构、异步物化视图、Colocate Join(docs.starrocks.io)

StarRocks 4.0 Release Notes(2025-10:File Bundling、DECIMAL256、ASOF JOIN、Iceberg compaction);镜舟 4.0 发布解读

Apache Doris 官方文档:Unique 模型 MoW/MoR(1.2 引入、2.1 默认启用)

CSDN《Apache Doris 与 StarRocks 的深度对比》(fork 史实:2020、0.13 前、DorisDB 更名)

AWS《StarRocks 3.0 存算分离最佳实践》;京东云 OSS 部署文档;京东物流降本案例(存储 -90%)

本系列前篇:《数据湖与湖仓一体》(external catalog 与冷热分层)、《数仓分层设计》(TTL 与 DWS)、《事实表三种类型》(最新态 vs 按日快照)、《缓慢变化维SCD》(维表进引擎后的口径纪律)

深度研究 · 编译:智柴 · 2026-09-08 · 源文件:StarRocks_MPP引擎_深度研究_2026-09-08.md

#StarRocks #Doris #ClickHouse #MPP #OLAP #实时数仓 #渠道数据 #智柴

👍 1
合作

智谱 GLM-5 已上线

在智谱开放平台 BigModel.cn 打造 AI 应用。新一代旗舰模型 GLM-5 在推理、代码、智能体综合能力达到开源模型 SOTA。

领取 2000万 Tokens