首页/数据/数仓建模速查/事实表去重与幂等写入
维度与事实设计149 条

事实表去重与幂等写入

上游重复投递与任务重跑都会产生重复事实行;用业务主键唯一约束或写前去重(row_number 按更新时间取最新)保证幂等。

数仓建模速查数据更新于 2026-10-13

事实表去重与幂等写入

事实表出现重复行有两个主要来源:上游重复投递,以及任务失败后的重跑。去重的通用手段是按业务主键取最新版本,常用写法是用窗口函数按更新时间倒序取第一行,再覆盖写入目标分区;同时给业务主键建唯一索引做兜底。判断实现是否合格的标准很直接:同一批数据连跑两次,行数与金额都不变。依赖人工清理重复数据的流程,迟早会在无人值守的夜间调度里出问题。

基本信息

重复来源上游重复投递、任务重跑、多批并行写入
去重手段按业务键取最新(row_number 按更新时间倒序)
兜底约束业务主键唯一索引或唯一键
幂等标准同批重跑两次,行数与汇总金额一致

典型使用场景

  • 夜间调度重跑:失败重试不需要人工清表,覆盖写入自动幂等
  • 上游消息重复:消息队列至少一次投递语义下,去重是必需环节
  • 上游补数:历史批次重新下发时按业务键取最新,避免新旧并存
  • 金额核对:去重口径统一后,汇总金额与源系统可按日对上
  • 多任务并行写入:同一分区被两个任务写入时,靠唯一约束兜底

按业务键覆盖写入

按业务键覆盖写入
-- 覆盖写目标分区,重跑结果一致(幂等)
INSERT OVERWRITE TABLE fact_order PARTITION (dt = '2026-10-11')
SELECT order_id,                 -- 业务主键
       user_sk, item_amt, create_time
FROM (
  SELECT *,
         row_number() OVER (PARTITION BY order_id
                            ORDER BY update_time DESC) AS rn  -- 取最新版本
  FROM stg_order
  WHERE dt = '2026-10-11'
) t
WHERE rn = 1;                    -- 每个业务键只保留一行

-- 兜底:给业务主键建唯一索引,重复写入直接失败而非静默累积
-- ALTER TABLE fact_order ADD CONSTRAINT uk_order UNIQUE (dt, order_id);

去重写法对照

写法是否幂等适用情况
覆盖写目标分区幂等分区粒度清晰,推荐默认用法
按业务键取最新后追加不幂等流式场景,需配合唯一约束
先删同批再插入幂等删除条件需精确到批次与业务键
全表重建幂等但代价高小表可用,大表资源消耗过大
去重取哪个版本要有明确口径:业务更新时间与入库时间在补数场景下差别明显,按入库时间取最新会把补的历史数据判为最新,造成口径倒退。
去重后仍要校验:按业务键分组计数,确认最大计数为一,这是防止上游投递语义变化最直接的巡检项。

易错点

  • 重跑直接追加:金额成倍虚增,且分布看起来合理不易察觉
  • 去重取错版本(按入库时间而非业务更新时间):补数后口径倒退
  • 只去重不建唯一约束:上游语义一变就重新出现重复行
  • 去重键选得太粗(只用订单号忽略商品行):合法的多行数据被误删

数据来源与参考

  • 事实表加载实践:幂等
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌