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

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

文本版 · 供搜索与朗读

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