首页/数据/数据工程与 ETL 速查/幂等写入与 Upsert 落库
管道基础52 条

幂等写入与 Upsert 落库

重跑任务时结果不产生重复或错误累计,就是幂等。常见做法:按业务主键 upsert、按分区覆盖写入、写临时表后原子替换。

幂等写入是什么

同一批数据重复写入多次,最终结果与只写一次完全一致,就称为幂等写入。它是数据管道里最实用的容错手段:任务失败后不必精确知道执行到哪一步,直接整段重跑即可,重复部分会被自然收敛掉。

三种常用手段

一是按业务主键 upsert,重复记录覆盖而不新增;二是分区覆盖写入,重跑时先按分区删再写,保证该分区的数据只有这一次作业的结果;三是写临时表后原子切换(或重命名为正式表),失败时正式表不受影响。三者共同的前提是存在稳定的主键或分区键。

基本信息

目标同一批数据重跑多次,结果保持一致
常用手段主键 upsert、分区覆盖、临时表切换
前提有稳定的业务主键或分区键
配套与至少一次投递组合,可逼近精确一次的效果
反面情况追加写入、自增主键判重、跨天全表覆盖

典型使用场景

  • 实时链路至少一次投递后,落库端用主键 upsert 收敛重复
  • 离线日批重跑:按当天分区整体覆盖,不需要逐条判断是否需要更新
  • 维表加工:写临时表校验通过后再重命名,避免下游读到中间态
  • 补数场景:重刷受影响的历史分区,其他分区不受影响
  • 多来源合并落地:不同来源写同一张表时按主键归并

MySQL 幂等写入

MySQL 幂等写入
-- 主键冲突时更新,重复写入结果一致
INSERT INTO dwd_order (order_id, amount, updated_at)
VALUES (%s, %s, %s)
ON DUPLICATE KEY UPDATE amount = VALUES(amount), updated_at = VALUES(updated_at);

数仓分区覆盖

数仓分区覆盖
-- 只覆盖当天分区,重跑多少次结果都相同
INSERT OVERWRITE TABLE dwd_order PARTITION (dt = '2026-10-11')
SELECT order_id, amount, updated_at FROM ods_order WHERE dt = '2026-10-11';

-- 注意:不写 PARTITION 子句的 INSERT OVERWRITE 会清空整表重建

三种手段对比

手段适用代价
主键 upsert实时流落库、单条粒度更新需要唯一键与索引,写入有额外开销
分区覆盖日批、按天分区的明细与汇总只对整分区有效,无法只更新分区内一行
临时表切换对外提供查询的表结构变更或整表重算需要额外存储与一次原子重命名操作
不带分区子句的 INSERT OVERWRITE 会清空整张表重建,误用一次就是全量事故;写这条语句前必须确认分区表达式存在且取值正确。
幂等与事务解决的是不同问题:幂等保证「重跑不产生重复」,事务保证「多次读写要么全成功要么全失败」。跨库写目标环境时,先做到幂等,再考虑事务边界。

易错点

  • 幂等键选错(用自增 ID 或写入时间当主键),重跑照样产生新行,看起来做了幂等实际没有
  • 只覆盖当前分区,但源端会补改历史日期的数据,导致历史分区长期错误,需要按变更日期回刷受影响分区
  • 对分区覆盖的表做分区内单行更新,只能整分区重写,数据量大时窗口跑不完
  • 先删后写的实现里删除成功、写入失败,中间窗口下游读到空数据,应改为写临时表再切换

数据来源与参考

  • MySQL 8.0 Reference Manual: INSERT ... ON DUPLICATE KEY UPDATE
  • Apache Hive LanguageManual DML
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌