跳到主要内容

03.维度建模入门:星型模型 vs 雪花模型 vs 星座模型

一、前言​

1.1 逻辑二维表​

数据仓库中,数据以表的形式存在,形式如下: 二维表

称为“逻辑表”,主要原因是在实际存储的时候,不一定是按照我们看到的形式存储的,会有各种形式的存储优化(例如列存)。 但是在建模的时候,二维表是我们思考的基本单位。

数据仓库建模就是 定义这些表的内部结构和表之间的关联关系。 一张表属于什么表,取决于其存储的内容,而不是其表名。表名只是方便我们识别和管理,而不是决定表的类型。

重要的事情再讲一遍: 无论是 维度表、事实表、桥接表、宽表、竖表、全量表、增量表、拉链表,我们都是在讲其存储的内容,内容的组织形式,而不是表的名字,表的名字是为了方便方便识别和管理。

1.2 星型模型、雪花模型、星座模型 在讲什么​

  1. 这三种模型属于维度建模方法,不是独立的建模方法论;
  2. 这三种模型描述的是二维表的关联形态,其描述的很形象,表关联起来的图形像什么就叫什么模型。(思考:三种模型描述了表关联形式,但是表为什么要关联、表什么时候关联?)
  3. 表内部结构决定了关联形态,关联形态决定了如何设计表内部结构。

二、先理解维度建模的两类表​

讲模型之前,必须先分清维度建模里的两类表,分别是事实表和维度表。

表类型存什么特点举例
事实表 (Fact)业务过程产生的可度量数值行数巨大、持续增长、窄而长订单金额、下单数量
维度表 (Dimension)描述业务事件的上下文属性行数少、相对稳定、宽而短用户、商品、时间、地区

事实表回答「发生了什么,量是多少」,维度表回答「发生在谁身上、什么时候、什么渠道」。两者通过外键关联:事实表里存一堆维度表的主键,查询时 JOIN 起来。

┌──────────────┐
│ dim_user │ ← 维度表(谁)
│ user_id(PK) │
│ user_name │
└──────┬───────┘
│ user_id
┌──────▼───────┐
│ fact_order │ ← 事实表(发生了什么)
│ order_id │
│ user_id(FK) │
│ product_id │
│ amount │ ← 度量(可加)
└──────┬───────┘
│ product_id
┌──────▼───────┐
│ dim_product │ ← 维度表(什么)
│ product_id │
│ category │
└──────────────┘

二、星型模型 (Star Schema)​

2.1 长什么样​

星型模型是最经典、最常用的维度模型。它的结构是:一张事实表居中,周围一圈维度表直接连上来,没有中间层,形似星星。

dim_date
│
dim_user ──── fact_order ──── dim_product
│
dim_region

特征:

  • 一张事实表 + N 张维度表,全部通过外键直接关联
  • 维度表之间不互相连接
  • 维度表不规范化(允许冗余),比如 dim_user 里同时存 city 和 province,而不是拆成地区维度

2.2 为什么是主流​

星型模型被 Kimball 学派捧为「默认选择」,核心就一句话:查询简单、性能好。

  1. JOIN 少:任何分析都只需事实表 + 少量维度表,最多 N+1 张表 JOIN,通常 2-3 张
  2. SQL 直观:分析师一眼能看懂,不需要理解复杂的规范化层级
  3. 列存友好:宽维度表在列存引擎下压缩率高、扫描快
-- 星型模型下,查「各城市订单总额」只需 2 张表
SELECT r.province, SUM(f.amount) AS total_amount
FROM fact_order f
JOIN dim_region r ON f.region_id = r.region_id
WHERE f.order_date >= '2026-01-01'
GROUP BY r.province;

2.3 代价​

维度表不规范化,意味着冗余。dim_user 里 province 字段在 1000 万用户里重复出现,占存储。不过在现代列存 + 字典编码下,这种冗余的成本几乎可以忽略——这也是星型「赢」了雪花的关键原因之一。


三、雪花模型 (Snowflake Schema)​

3.1 长什么样​

雪花模型是星型的「规范化版本」:维度表继续往下拆成多级子维度表,形成层级,形似雪花。

dim_date
│
dim_user ──── fact_order ──── dim_product
│ │
dim_city ── dim_province dim_category ── dim_brand

比如 dim_user 不再存 city、province,而是只存 city_id,然后 city 又指向 province。维度表被拆到第三范式(3NF)。

3.2 优点​

  • 消除冗余:地区、分类这类层级信息只存一份
  • 维护一致性:改了 dim_province 的名字,所有下游自动生效,不会出现「同一省份两种写法」

3.3 为什么实际很少用​

雪花模型在理论上「更干净」,但工程上几乎被星型碾压:

维度星型雪花
JOIN 数量少多(每级子维度多一个 JOIN)
查询性能快慢(JOIN 是数仓最大开销)
SQL 复杂度低高(分析师要懂层级)
存储冗余有无
维护成本中低(规范化)

关键结论:雪花省下的那点存储,远不够抵消多出来的 JOIN 开销。维度表本身数据量就小(用户维度几千万行在数仓里算「小表」),冗余的代价极低;而 JOIN 是数仓查询的绝对瓶颈。

3.4 雪花模型的少数适用场景​

雪花模型并非一无是处,两种情况下可以局部采用:

  1. 维度层级很深且查询频繁按层级钻取——比如地区有「国家→省→市→区」四级,且 BI 经常要按省、市逐层下钻
  2. 维度表本身巨大且层级稳定——规范化能明显省存储

但即便用,也通常只在局部雪花化(只有一两个维度拆),而不是全盘雪花。


四、星座模型 (Constellation Schema / 星系模型)​

4.1 长什么样​

星座模型(也叫星系模型、事实星座)是多张事实表共享维度表的结构,形似多个星型交叠在一起。

dim_date
/ \
dim_user ─ fact_order fact_payment ─ dim_channel
\ | |
\ dim_product ────────┘
\ |
fact_refund ── dim_region

注意:dim_user、dim_date、dim_product 被 fact_order、fact_payment、fact_refund 三张事实表共享。

4.2 为什么需要星座模型​

真实数仓从来不是只有一张事实表。电商有订单事实、支付事实、退款事实、浏览事实、加购事实……它们共享同一套维度(用户、时间、商品、地区)。

星座模型解决的核心问题是:让不同业务过程的事实表,共享一致的维度,从而支持跨域分析(比如「各渠道的 GMV」= 订单事实 JOIN 渠道维度 + 支付事实 JOIN 渠道维度)。

4.3 核心:一致性维度 (Conformed Dimension)​

星座模型能成立的前提是一致性维度:被共享的维度表,在所有事实表里定义完全一致(相同的字段名、相同的取值域、相同的语义)。

fact_order.user_id ┐
fact_payment.user_id ├── 都指向同一张 dim_user,且语义一致
fact_refund.user_id ┘

如果订单事实里的 user_id 和支付事实里的 user_id 不是同一套编码,JOIN 出来就是笛卡尔积垃圾。

这正是 数据仓库建模原则 里「维度一致才能跨域分析」这条原则的由来。


五、三种模型对比总表​

维度星型模型雪花模型星座模型
事实表数量1 张1 张多张
维度表是否规范化否(冗余)是(3NF 层级)取决于维度
JOIN 数量少多中
查询性能高低高
存储有冗余省存储有冗余
适用场景单一业务过程维度层级深的局部企业级多业务过程
本质基础形态星型的规范化变体多个星型共享维度

一句话记忆:

  • 星型 = 一张事实表 + 一圈维度
  • 雪花 = 星型里维度继续往下拆
  • 星座 = 多张事实表共享同一批维度

六、小结​

  • 星型模型:一张事实表 + 一圈维度,查询快、SQL 简单,是默认选择
  • 雪花模型:维度规范化,省存储但 JOIN 多,实际少用,只在局部出现
  • 星座模型:多事实表共享一致性维度,企业级多业务过程的必然形态
📱关注公众号

「数据仓库技术」文章同步更新,不错过每一篇干货

微信公众号二维码
💬加群交流

备注「数据仓库技术」加入社群,每日一道大厂SQL真题

交流微信二维码

你可能还想看