外观
快照表 vs 拉链何时用哪个
难度与前置
难度:入门
前置:知道「只要最新」和「要历史时点」的差别。
读完能做什么:用一张决策表为渠道/用户等维表选型,并给出存储与查询验收。
适用场景
维表或缓变属性要入库,纠结:
- 每天落一份全量快照(
dim_xxx_df) - 还是做 SCD2 拉链(开链闭链)
不适用:
- 事实明细流水(事件表)——不是维表选型问题
- 源系统已提供完整变更日志且下游只要事件——优先落地变更明细,再决定是否衍生拉链
- 属性几乎每日大变且要保留每次变更——评估「变更明细 + 按需快照」而不是盲上拉链
真实场景(一条)
投放 渠道维:字段含结算主体、合作状态、运营负责人。两种用法并存:
- 日报:按「入账日当天」的结算主体汇总(要历史对齐)
- 运营后台:只展示渠道当前负责人与状态
若只存最新表:历史日报重跑会串主体。若只存拉链却每天给后台扫全链:查询复杂且易用错 is_current。常见落地:拉链为真相源 + 每日当前快照(或视图)服务在线,或对「几乎不追溯」的属性用快照即可。
怎么做(步骤清单)
1. 用三个问题做选型
| 问题 | 偏快照 | 偏拉链 |
|---|---|---|
| 是否要回答「某日当时是什么」? | 偶尔;可接受重跑用当日快照近似 | 经常;且要对齐业务日 |
| 变更频率 | 很低或只关心最新 | 中低,但变更要留史 |
| 存储与治理成本 | 可接受按日存全量 | 希望一行多版本、占用更紧 |
补充:
- 只要最新 → SCD1 / 单分区覆盖快照
- 要每日切片且属性多、变更少 → 日快照简单直观
- 要任意时点 + 变更可审计 → SCD2 拉链
- 一天多变且要顺序 → 变更明细(事件)± 拉链(日精度不够时勿只用日拉链)
2. 对照表(写进设计文档)
| 维度 | 日全量快照 *_df | SCD2 拉链 |
|---|---|---|
| 一行含义 | 某实体在 dt 的完整属性 | 某实体一个版本区间 |
| 查某日状态 | WHERE dt = biz_date | valid_from <= t < valid_to |
| 查当前 | dt = max 或最新分区 | is_current = true 或 valid_to = 无穷 |
| 存储 | O(天 × 实体数) | O(实体数 + 变更次数) |
| 回刷 | 覆盖若干 dt 分区 | 要处理断链/重叠(见拉链专篇) |
| 典型坑 | 历史分区丢了就丢史 | 区间重叠、闭链遗漏 |
3. 推荐组合(多数业务)
text
源维变更
→ dwd/dim 变更明细(可选,便于审计)
→ dim_channel_zipper(SCD2,真相源)
→ dim_channel_df(每日从拉链切「当日有效」快照,供简单 Join)日快照生成示意:
sql
-- 从拉链切某业务日快照(左闭右开)
INSERT OVERWRITE TABLE dim_channel_df PARTITION (dt = '${biz_date}')
SELECT
channel_id,
settle_entity,
coop_status,
owner_id
FROM dim_channel_zipper
WHERE valid_from <= '${biz_date}'
AND '${biz_date}' < valid_to;若变更极少且团队不熟拉链:先日快照,等第一次「重跑串主体」事故后再上拉链,并保留快照作加速层。
4. 下游 Join 约定
- 事实表按 业务日 对齐维:优先
fact.dt = dim_df.dt - 必须用拉链时:在 ETL 内 Join 并物化到 DWD/DWS,禁止每个 ADS 手写区间条件(易写错)
- 文档写清:对齐的是「入账日属性」还是「事件发生时属性」
5. 成本粗算(决策用)
text
快照年存储 ≈ 实体数 × 字段宽 × 365 × 副本
拉链年存储 ≈ 实体数 × 字段宽 × (1 + 年变更率 × 平均版本长)实体 10 万、年变更率 5%、只要当前+偶发追溯:拉链更省。实体 1 万、天天要「昨日全貌」给多团队乱 Join:快照更省心。
6. 迁移路径(快照 → 拉链)
已有多年 dim_xxx_df、现在要上拉链时:
- 用相邻快照 diff 生成「疑似变更」初始化拉链(注明近似,日内多变会丢)
- 从某日起切到「变更源 → 拉链 → 派生快照」
- 历史报表继续读旧快照分区;新逻辑读拉链派生
- 双跑对比 2 周再切流量
不要幻想「只建空拉链、历史自然出现」——历史要靠快照回放或源端变更日志。
常见坑(至少牢记这些)
该追溯却只用最新表
历史重跑串结算主体,对账永远扯不清。有「按日属性」需求就上快照或拉链。上了拉链仍让每人手写区间
条件写反、忘右开,导致双匹配。对外提供dim_df或标准 UDF/视图。快照当流水用
用相邻两日dtdiff 当变更审计,却缺日内多次变更。要审计就落变更明细。拉链日精度覆盖「一日多变」
同一天两次改负责人,日粒度拉链只能留一版。升到时间戳或事件表。两套真相源长期双写不一致
快照与拉链各算各的。必须规定:单一真相源,另一份派生。
如何验收(可量化)
- [ ] 设计文档有选型表与三个问题的答案
- [ ] 真相源唯一;派生快照有任务依赖
- [ ] 抽 3 个曾变更实体:快照日切片与拉链区间查询一致
- [ ] 下游禁止「ADS 直接扫拉链各写各的」或已有标准视图
- [ ] 回刷演练:改一版历史属性,级联快照/报表后对账可解释
sql
-- 验收:同一 biz_date,快照 vs 拉链切片一致
SELECT
COALESCE(a.channel_id, b.channel_id) AS channel_id,
a.settle_entity AS snap_entity,
b.settle_entity AS zip_entity
FROM (
SELECT channel_id, settle_entity
FROM dim_channel_df WHERE dt = '${biz_date}'
) a
FULL OUTER JOIN (
SELECT channel_id, settle_entity
FROM dim_channel_zipper
WHERE valid_from <= '${biz_date}' AND '${biz_date}' < valid_to
) b ON a.channel_id = b.channel_id
WHERE a.settle_entity IS DISTINCT FROM b.settle_entity
OR a.channel_id IS NULL OR b.channel_id IS NULL;
-- 期望:0 行sql
-- 验收:拉链无重叠区间(同主键)
SELECT channel_id, COUNT(*) AS overlap_pairs
FROM (
SELECT a.channel_id
FROM dim_channel_zipper a
JOIN dim_channel_zipper b
ON a.channel_id = b.channel_id
AND a.valid_from < b.valid_from
AND b.valid_from < a.valid_to
) t
GROUP BY channel_id;
-- 期望:无行相关面试题
占位:与题库小程序对齐后补链接(建议关联:SCD1/2、快照与拉链选型、维表回刷)。
改写说明:结构参考 Wood 原创建模实践,已改写成可执行清单;未包含未公开公司维表名。