事实表去重与幂等写入
事实表出现重复行有两个主要来源:上游重复投递,以及任务失败后的重跑。去重的通用手段是按业务主键取最新版本,常用写法是用窗口函数按更新时间倒序取第一行,再覆盖写入目标分区;同时给业务主键建唯一索引做兜底。判断实现是否合格的标准很直接:同一批数据连跑两次,行数与金额都不变。依赖人工清理重复数据的流程,迟早会在无人值守的夜间调度里出问题。
基本信息
| 重复来源 | 上游重复投递、任务重跑、多批并行写入 |
|---|---|
| 去重手段 | 按业务键取最新(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);去重写法对照
| 写法 | 是否幂等 | 适用情况 |
|---|---|---|
| 覆盖写目标分区 | 幂等 | 分区粒度清晰,推荐默认用法 |
| 按业务键取最新后追加 | 不幂等 | 流式场景,需配合唯一约束 |
| 先删同批再插入 | 幂等 | 删除条件需精确到批次与业务键 |
| 全表重建 | 幂等但代价高 | 小表可用,大表资源消耗过大 |
去重取哪个版本要有明确口径:业务更新时间与入库时间在补数场景下差别明显,按入库时间取最新会把补的历史数据判为最新,造成口径倒退。
去重后仍要校验:按业务键分组计数,确认最大计数为一,这是防止上游投递语义变化最直接的巡检项。