1、数据回填概述:什么是数据回填、回填与补数的区别、回填的典型业务场景
大家好,我是老张。做了快十年调度系统,今天咱们聊聊数据回填。
说实话,刚入行那会儿我也分不清回填和补数有什么区别。直到有一次凌晨三点被电话叫醒,说离线数仓的指标全乱了……嗯,那次之后我才真正搞明白这两个概念。
1.1 什么是数据回填
数据回填,说白了就是把历史时间段里缺失或错误的数据,重新计算并写回去。
举个例子:你每天凌晨跑一次离线ETL,计算前一天的销售总额。结果某天发现上游业务表少传了2小时的数据。等数据补上来之后,你需要把那天重新算一遍,把正确的结果覆盖回去——这就是回填。
核心特征:
- 操作对象是历史分区(昨天、前天、甚至上个月)
- 触发原因是数据质量或链路异常
- 执行结果是覆盖已有数据
我在项目中遇到过最典型的场景:实时数仓的Kafka链路挂了6小时,恢复后需要把这段时间的流式数据重新从ODS层拉取、计算、写入DWS层。那次回填涉及200多个任务,调度依赖关系像蜘蛛网一样复杂。
1.2 回填与补数的区别
很多同学把回填和补数混为一谈。我刚开始也这样,直到被架构师怼了一次才记住。
| 维度 | 数据回填 | 数据补数 |
|---|---|---|
| 触发时机 | 数据已产出,但质量不合格 | 数据从未产出过 |
| 操作范围 | 覆盖已有分区 | 新增缺失分区 |
| 调度策略 | 需要清理旧数据再写入 | 直接追加写入 |
| 典型场景 | 上游数据延迟、逻辑变更 | 新接入数据源、历史初始化 |
| 风险等级 | 高(可能影响下游依赖) | 中(通常无覆盖风险) |
举个例子你就明白了:
- 补数:你新接了一个支付渠道,需要把过去3个月的历史数据拉回来。这些分区之前不存在,直接写进去就行。
- 回填:支付渠道的数据一直有,但昨天发现某个字段解析错了。你需要把昨天的分区删掉,重新跑一遍任务,再把正确结果写回去。
避坑指南:我曾经在回填时忘了清理旧分区,结果新旧数据混在一起,下游报表出现了"数据跳变"。后来我养成了一个习惯:回填任务的第一步必须是TRUNCATE或DELETE目标分区。
1.3 回填的典型业务场景
回填不是天天发生,但一旦发生,往往都是紧急情况。我总结了两大类最常见的场景。
场景一:离线数仓回填
离线数仓是回填的"重灾区"。为什么?因为链路长、依赖多、任何一个环节出问题都会导致数据异常。
- 上游数据延迟:业务库凌晨3点才把前一天的全量数据推过来,但你的ETL任务凌晨1点就跑完了。怎么办?等数据到了之后回填。
- 逻辑变更:产品经理说"昨天的转化率口径不对,需要按新规则重算"。这时候你需要把昨天的分区全部回填。
- 数据质量修复:发现某个字段有NULL值,或者join丢了数据。定位问题后,需要回填受影响的时间段。
我的经验:离线数仓回填最怕的是"级联回填"。你回填了DWD层,DWS层和ADS层也得跟着回填。所以调度系统必须支持下游依赖自动触发,否则人工操作容易漏掉。
场景二:实时链路修复
实时链路出问题,回填的难度比离线大得多。因为实时数据是流式的,没有"分区"的概念。
- Kafka消息积压或丢失:实时ETL任务挂了2小时,恢复后需要把这2小时的流式数据重新消费一遍。
- 状态不一致:Flink任务重启后,状态后端的数据丢了。需要从Checkpoint或Savepoint恢复,同时回填丢失时间段的数据。
- 实时转离线:有些场景是"实时+离线"双链路。实时链路挂了,离线链路还在跑。等实时链路恢复后,需要把离线数据回填到实时表中。
我记得有一次,实时数仓的Flink任务因为OOM挂了。恢复之后发现状态全丢了,窗口聚合结果全部归零。最后只能从Kafka重置offset到故障时间点,重新消费一遍数据。那次回填跑了整整8个小时。
1.4 回填的核心流程
不管什么场景,回填的流程都差不多。我画了一张图,你看一眼就明白了。
这个流程看起来简单,但实际落地时坑很多。比如第三步"清理旧数据",如果你用的是Hive分区表,直接ALTER TABLE DROP PARTITION就行。但如果是Kudu或ClickHouse,清理方式完全不同。
回填的黄金法则:
- 先清理,再写入——避免数据重复
- 记录回填日志——方便追溯和回滚
- 通知下游依赖——防止下游基于错误数据做计算
1.5 小结
数据回填是调度系统里最考验设计能力的场景之一。它不像日常调度那样按部就班,而是需要处理"历史数据修正"这个特殊需求。
记住三个关键点:
- 回填是覆盖,补数是新增,别搞混
- 离线数仓回填关注分区管理,实时链路回填关注状态恢复
- 回填一定要先清理再写入,这是血的教训