【从零开始学习数据仓库 | 第一篇】:维度建模理论

写在前面:这是我学习数仓系列记录的第一篇,我会把每天学习时对工作重要的,难理解的东西记录下,我试着把 Kimball 维度建模的核心概念串成一条线------从"为什么数仓必须独立存在"讲起,再落到维度表、事实表、代理键、可加性这些基础零件上。文中会穿插一些我自己的理解方式,不一定严谨。


一、OLTP 与 OLAP:为什么数仓必须独立存在

刚接触数仓时我有个疑问:业务库好好的,为什么不能直接拿来做分析?答案藏在两类系统的基因差异里。

OLTP(联机事务处理) 的设计目标是"快而准地处理每一笔交易"。它高度规范化,同一个客户信息只存一份,更新时只改一个地方。这在写入场景下很高效,但一旦面对"统计上个月华东地区所有品类销售额"这类查询,就需要把十几张规范化表 Join 起来,慢得让人绝望。

OLAP(联机分析处理) 的设计目标恰恰相反:让分析查询跑得快。它采用维度建模,把数据组织成星型或雪花型,适度反规范化,用冗余换取更少的 Join 和更快的聚合。而且分析往往要回溯历史,OLTP 库如果为了性能定期清理旧数据,分析就没法做了。

更现实的问题是资源争抢。一条扫描几百万行的分析查询扔到业务库上,会直接拖慢用户的正常下单和支付。所以数仓独立存在,本质上是在保护交易系统的稳定性,同时给分析工作一个按分析场景优化过的环境。这不是架构上的奢侈,是工程上的必要。


二、数仓 vs 数据库:不只是名字不同

把"数仓"和"数据库"混着叫,是我入门时最常犯的错。现在我会这样区分:

数据库(偏 OLTP) 关心的是"当前这一刻,这笔订单的状态是什么"。它追求事务的 ACID,数据是规范化的,保留的是最新的、一致的状态快照。

数据仓库(偏 OLAP) 关心的是"过去三年,这类订单的趋势是什么"。它追求的是查询吞吐和聚合效率,数据是反规范化的,保留的是完整的历史轨迹。一个客户搬了三次家,OLTP 库里只有最新地址,但数仓里可能有三条记录,分别对应他每次下单时所在的地址。

另外,数仓是面向主题的。业务库按"订单表、用户表、商品表"这样按功能划分,数仓则按"销售主题、库存主题、用户行为主题"来组织,把散落在多个业务系统里、关于同一主题的数据集成到一起。这种集成本身就需要清洗、去重、统一口径,不是简单复制能完成的。


三、维度表与事实表:数仓的两块基石

维度建模把所有表分成两类,这个二分法极其简洁,但足以覆盖绝大多数分析场景。

事实表 记录的是"发生了什么"。每一行对应一个业务事件(一次下单、一笔支付、一条库存快照),里面装的是可度量的数字,比如金额、数量、次数。事实表通常又窄又长,行数可以大到千亿级。

维度表描述的是"谁、什么、哪里、何时"。它给事实表里的数字提供上下文。比如事实表里有一行"2024-03-15,客户123,商品456,买了2件,花了199元",维度表负责告诉你:客户123住在杭州、是金卡会员;商品456属于户外运动品类、是某个品牌的帐篷;2024-03-15是周五、属于第一季度。

我自己的记忆方法是:事实表是"动词+数字",维度表是"名词+描述"。事实表回答"多少",维度表回答"关于什么"。


四、自然键 vs 代理键:为什么维度表需要两个身份证

维度表里通常有两个"键",一开始我觉得这是冗余,后来才明白各自的分工。

自然键 (也叫业务键)是业务系统里本来就有的标识,比如订单号、身份证号、商品编码。它来自源系统,对人友好,但有个致命问题:可能变,也可能重复。比如商品编码在系统升级时改规则了,或者两个源系统的客户 ID 撞号了。

代理键 是数仓自己生成的、无业务含义的整数(通常自增)。它唯一的职责就是在数仓内部唯一标识一行 。用代理键做 Join 有几个实际好处:整数比字符串 Join 快;源系统的键变了不影响数仓;更重要的是,它支持缓慢变化维度------同一个客户因为搬家产生了三条历史记录,自然键都是同一个身份证号,但代理键分别是 101、102、103。这样,搬家前下的单关联的是 101,搬家后关联的是 102,历史归属就保住了。

我的理解是:自然键是"给业务看的",代理键是"给数仓自己用的"。


五、关于"维度设计六原则"的说明

Kimball 原著和主流实践里并没有一个被广泛公认的、固定编号的"六原则"。不同团队会根据自己的经验总结出不同条数。结合常见实践,大致可以归纳为:

  1. 原子性:维度属性尽量不可再分,日期就存日期,不要存"2024年3月"这种混合值。
  2. 一致性:同一个业务概念在不同维度表里的命名、类型、枚举值必须统一,否则跨主题分析会错。
  3. 可理解性:属性用文字描述而非编码,让业务用户能直接看懂。
  4. 适当冗余:为了少 Join,维度表可以反规范化,把层次属性打平放在同一张表里。
  5. 可扩展性:预留未来可能增加的属性位置,避免频繁改表。
  6. 代理键独立:维度表必须有代理键,与业务键分离。

需要说明,这不是金科玉律,更像是一个检查清单。不同书和文章里的"原则"条目差异很大,不必纠结于数字。


六、事实的三种可加性

事实表里的数字,不是都能随便相加的。这个区分在做汇总时至关重要。

可加事实:可以沿任意维度相加。销售额、数量、次数,怎么加都对。

半可加事实 :只能沿部分维度相加,最典型的是余额。你可以把同一天所有账户的余额加起来(沿账户维度可加),但不能把一个账户一个月的余额加起来(沿时间维度不可加,那没有意义)。

不可加事实 :任何维度都不能直接相加,比如比率(转化率、毛利率)。处理办法是拆成分子和分母分别存储,让它们变成可加事实,使用时再相除。比如不存"转化率 3%",存"曝光 1000 次,转化 30 次"。

这个知识点看起来小,但在实际做报表时,直接把比率字段 sum() 起来是最常见的错误之一。


七、三种事实表:对应三类业务节奏

事实表按业务过程的时间特性,分成三种。我一开始分不清后两种,后来用"一个事件 vs 一个周期 vs 一个生命周期"来记,就清楚多了。

事务事实表 是最基础的。业务发生一次,插入一行,只插入,不更新。下单、支付、点击、退款,每个原子事件一行。粒度最细,灵活性最高。

周期快照事实表按固定周期(天/周/月)记录状态。比如每天日终的库存余额、每月末的账户余额。它不是记录"发生了什么变化",而是记录"在那一时刻,状态是什么"。同样只插入,不更新,但粒度是"实体 × 周期"。

累积快照事实表 最特殊,它跟踪一个实体的完整生命周期 。一个订单从创建、付款、发货到签收,在累积快照表里是同一行 ,随着流程推进不断被 UPDATE,不同阶段的日期填进不同字段。它的价值在于能直接算出"下单到付款平均间隔多久"这类跨阶段指标。

三种表的核心区别:

事务事实表 周期快照事实表 累积快照事实表
一行代表 一个事件 一个实体在一个周期内的状态 一个实体的完整生命周期
写入方式 只插入 只插入 插入后更新
时间字段 单一事务时间 单一快照时间 多个里程碑时间

写在最后

这篇笔记梳理了维度建模最底层的几个概念。我的体会是:维度建模的价值不在于概念多精妙,而在于它强迫你在动手写 SQL 之前,先把"业务过程是什么、粒度是什么、谁在分析什么"想清楚。代理键、可加性、三种事实表,这些看似琐碎的规则,每一条背后都有真实的踩坑教训。

相关推荐
TDengine (老段)3 小时前
TDengine IDMP 十大实用功能(2)
大数据·数据库·chrome·时序数据库·tdengine·idmp·有问有答
海宇AI3 小时前
零信任架构实战:基于海宇学籍核验三要素构建自动化文旅景区学生票核验网关
大数据·人工智能·架构·自动化
keyipatience4 小时前
Git学习(4)Git分布式优势与实战配置全解析
git·学习
Bode_20025 小时前
美的集团数智化创新‌
大数据·人工智能
瓶瓶持续学习中5 小时前
空间智能与物理AI:国内厂商如何与英伟达协同发展
大数据·人工智能·信息可视化·架构
阿乔外贸日记5 小时前
外贸风控|美国市场7大类欺诈模式解析与风控框架
大数据·人工智能·物联网·microsoft·智能手机
金融小师妹5 小时前
时间序列分析:历次联储利率调整与政策周期变化研究
大数据·人工智能·逻辑回归
Elastic 中国社区官方博客5 小时前
使用 OpenTelemetry 在 Elastic 中实现 Claude 平台的人工智能 agent 可观测性
大数据·运维·服务器·人工智能·elasticsearch·信息可视化·全文检索
GOWIN革文品牌咨询5 小时前
B2B圆桌内容化:问题链、记录与授权工作流
大数据·架构