Skip to content

快照表 vs 拉链何时用哪个 ​

难度与前置

难度:入门
前置:知道「只要最新」和「要历史时点」的差别。
读完能做什么:用一张决策表为渠道/用户等维表选型,并给出存储与查询验收。

适用场景 ​

维表或缓变属性要入库,纠结:

  • 每天落一份全量快照(dim_xxx_df)
  • 还是做 SCD2 拉链(开链闭链)

不适用:

  • 事实明细流水(事件表)——不是维表选型问题
  • 源系统已提供完整变更日志且下游只要事件——优先落地变更明细,再决定是否衍生拉链
  • 属性几乎每日大变且要保留每次变更——评估「变更明细 + 按需快照」而不是盲上拉链

真实场景(一条) ​

投放 渠道维:字段含结算主体、合作状态、运营负责人。两种用法并存:

  1. 日报:按「入账日当天」的结算主体汇总(要历史对齐)
  2. 运营后台:只展示渠道当前负责人与状态

若只存最新表:历史日报重跑会串主体。若只存拉链却每天给后台扫全链:查询复杂且易用错 is_current。常见落地:拉链为真相源 + 每日当前快照(或视图)服务在线,或对「几乎不追溯」的属性用快照即可。

怎么做(步骤清单) ​

1. 用三个问题做选型 ​

问题偏快照偏拉链
是否要回答「某日当时是什么」?偶尔;可接受重跑用当日快照近似经常;且要对齐业务日
变更频率很低或只关心最新中低,但变更要留史
存储与治理成本可接受按日存全量希望一行多版本、占用更紧

补充:

  • 只要最新 → SCD1 / 单分区覆盖快照
  • 要每日切片且属性多、变更少 → 日快照简单直观
  • 要任意时点 + 变更可审计 → SCD2 拉链
  • 一天多变且要顺序 → 变更明细(事件)± 拉链(日精度不够时勿只用日拉链)

2. 对照表(写进设计文档) ​

维度日全量快照 *_dfSCD2 拉链
一行含义某实体在 dt 的完整属性某实体一个版本区间
查某日状态WHERE dt = biz_datevalid_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、现在要上拉链时:

  1. 用相邻快照 diff 生成「疑似变更」初始化拉链(注明近似,日内多变会丢)
  2. 从某日起切到「变更源 → 拉链 → 派生快照」
  3. 历史报表继续读旧快照分区;新逻辑读拉链派生
  4. 双跑对比 2 周再切流量

不要幻想「只建空拉链、历史自然出现」——历史要靠快照回放或源端变更日志。

常见坑(至少牢记这些) ​

  1. 该追溯却只用最新表
    历史重跑串结算主体,对账永远扯不清。有「按日属性」需求就上快照或拉链。

  2. 上了拉链仍让每人手写区间
    条件写反、忘右开,导致双匹配。对外提供 dim_df 或标准 UDF/视图。

  3. 快照当流水用
    用相邻两日 dt diff 当变更审计,却缺日内多次变更。要审计就落变更明细。

  4. 拉链日精度覆盖「一日多变」
    同一天两次改负责人,日粒度拉链只能留一版。升到时间戳或事件表。

  5. 两套真相源长期双写不一致
    快照与拉链各算各的。必须规定:单一真相源,另一份派生。

如何验收(可量化) ​

  • [ ] 设计文档有选型表与三个问题的答案
  • [ ] 真相源唯一;派生快照有任务依赖
  • [ ] 抽 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 原创建模实践,已改写成可执行清单;未包含未公开公司维表名。