大批量任务的断点续跑:从“全量重跑”到“精确补偿”
在离线任务、批处理、账务结算等场景中,“任务中途宕机了,从哪儿接着跑?”是绕不过去的问题。
如果每次都从头开始重跑,不但浪费资源,还可能引发业务超时。
真正的考点是:如何在保证数据正确的前提下,做到精准续跑?
一、直觉方案:宕了就重跑一遍
最直观的做法是:
- 任务挂了 → 重启服务 → 从头重新把所有数据跑一遍。
问题:
-
数据量大的场景几乎不可接受
- 几千万条数据,每次异常都来一遍全量重算,成本极高。
-
时间不可控
- 跑满一次全量的时间往往远超业务能接受的窗口。
-
完全浪费前面已成功的工作
- 明明前 90% 已完成,却每次都要从 0 开始。
这种“无脑重跑”的策略,只适合作为最保守的兜底,不适合体面地出现在面试回答里。
二、核心思路:进度持久化 + 幂等保障
1. 进度持久化:用一张表记录“我处理到哪儿了”
-
在数据库中建一张
progress表,核心字段:task_id:任务标识offset:已经处理到的最后一条记录 ID 或偏移量- 其他如状态、更新时间等。
-
每处理完一批数据(例如 1000 条),就更新一次
offset。- 第一次:offset = 1000
- 第二次:offset = 2000
- …
-
宕机重启后,启动逻辑变为:
- 查询
progress获取当前 offset; - 下次从
offset + 1开始处理。
- 查询
好处:
- 能够做到“从中间接着跑”,而不是从头开始。
2. 幂等保障:重复执行也不出错
即便有 offset,仍然有这种情况:
业务处理成功,但更新
progress失败,重启后会重复处理这一批。
所以幂等性是刚需。典型做法:
- 利用数据库唯一索引,重复插入直接被忽略。
- 使用
INSERT IGNORE/ON DUPLICATE KEY UPDATE等语法,保证多次执行结果不变。 - 更新类操作设计为「幂等更新」:相同参数多次执行效果一致。
三、进度更新失败时如何权衡?
1. 默认策略:多处理一批,但结果不乱
- 若处理数据成功,但 offset 更新失败:
- 宕机重启后会从旧 offset 重复处理这一批数据。
- 在幂等设计下:结果不乱,只是多耗一点计算资源。
这是一种以少量性能换可靠性的设计,非常常见。
2. 进一步优化:将业务处理与进度更新放入同一事务
- 把“处理这一批数据 + 更新 offset”放到一个数据库事务中。
- 要么两者一起成功,要么一起回滚。
- 牺牲的是:
- 单次批处理事务变大,锁定时间变长。
- 但逻辑简单、状态更可控。
四、补充思路:批大小、重试与监控
在基础方案上,还可以从三个方向继续打磨:
-
合理控制批大小
- 批太大会导致单次事务时间过长,对锁和日志不友好。
- 批太小又会频繁更新 offset,增加开销。
-
为处理逻辑和 offset 更新增加重试
- 例如:网络抖动导致写库偶发失败时可以重试一两次。
- 重试仍失败时,记录错误日志供人工排查。
-
增加监控与告警
- 监控任务耗时、进度推进速度、失败次数。
- 避免“默默失败”,而业务方还以为任务已完成。
五、没有银弹,只有权衡
- 单纯“全量重跑”过于粗暴,只能当兜底。
- “进度持久化 + 幂等”是主流、务实的组合。
- 是否要将进度与业务放到同一事务,要在性能与一致性之间做权衡。
六、面试思路
-
先抽象问题:
- 宕机后如何断点续跑?本质是“进度管理 + 幂等”。
-
给出核心方案:
progress表持久化 offset;- 每批处理完更新 offset;
- 重启后从 offset+1 开始。
-
强调幂等:
- 利用唯一索引或
INSERT IGNORE做幂等,避免重复执行导致结果错乱。
- 利用唯一索引或
-
谈异常情况:
- 若任务成功但 offset 更新失败,会重复处理一批;
- 代价只是性能浪费,不影响最终结果。
-
可以再加一层优化:
- 把“处理 + 更新进度”放到一个事务里,根据业务量权衡。
3397

被折叠的 条评论
为什么被折叠?



