批处理系统失效恢复(二):幂等与断点续跑,让重跑不闯祸

系列索引:① 失效的发现与止血(已发)|② 幂等与断点续跑(本篇)|③ 任务编排与依赖图(计划中)

上篇说怎么在十分钟内发现批处理挂了、怎么止血。这篇说止血之后的事:重跑之前,有两道坎必须先过——幂等和断点。没过这两道坎就敢整个重跑的团队,迟早会在某个凌晨重复发一轮优惠券。

先说事故长什么样。凌晨两点,跑了两小时的任务在 70% 处挂掉。值班同学最顺手的操作是「整任务重跑」——然后凌晨三点,客服电话开始响:同一笔订单扣了两次款,同一个用户收到三条相同的短信。止血动作本身变成了二次事故。

幂等:重跑不闯祸

幂等的本质是「同一个业务动作执行一次和执行 N 次,效果一样」。落到工程上是三件套:

  1. 数据库唯一约束兜底。业务唯一键(订单号+动作类型)建唯一索引,重复写入直接被数据库拒绝。这是最后一道防线,比任何应用层判重都可靠。
  2. 版本号乐观锁。更新走 UPDATE task SET status='DONE', version=version+1 WHERE id=? AND version=?,判断受影响行数。版本不匹配说明别人(或上一轮的自己)已经处理过,直接跳过。
  3. 状态机约束。只允许「待处理→处理中→成功/失败」单向流转,重复投递的任务在入口处查状态,非「待处理」一律丢弃。

三件套里最容易被忽略的是第三条。很多系统的判重逻辑散落在各个消费者的 if 判断里,一旦加一个新的消费入口,判重就漏了。状态机收口到一处,新入口想绕都绕不开。

断点续跑:重跑不从头

70% 处挂掉的任务,从头跑意味着前面 70% 的成功结果要被重算一遍——不只是慢,还放大了幂等压力。解法是检查点(checkpoint)表:以任务粒度记录处理水位,文件类任务记录已处理文件的哈希清单,消息类任务记录消费 offset。任务重启时先读水位,从断点继续。

这里有个实践细节:检查点的写入必须和业务处理在同一个事务里。先干活后记水位,挂在水位写入前,重启就会重复处理;先记水位后干活,挂在中间就会漏处理。宁可重复不可遗漏的业务,把顺序反过来,然后用幂等兜住重复。

重触发:把重跑按钮交出去

最后一块拼图是调度平台的能力开放。XXL-JOB 这类调度框架的重触发 OpenAPI 是好范式:值班的同学在手机上对失败的分片点一下「重跑」,而不是登服务器、查表、手改状态。重触发接口天然带任务边界(只重跑失败分片),配合断点续跑,凌晨的事故响应可以从一小时缩到五分钟。

工具链生态也在往这个方向走——开源工具作者把「发布即有人用」的循环跑通后,批处理的运维面会越来越产品化。自己造轮子之前,先看看调度框架的 OpenAPI 已经覆盖到哪。

小结:幂等保证重跑不闯祸,断点保证重跑不从头,重触发接口保证重跑不需要人肉。三件事齐了,「失败重跑」才敢写进值班手册变成标准动作。


写文里这些翻译场景,来自我日常在用的「随心翻译」:Edge/Chrome 浏览器插件,本地优先架构,不用不花钱。官网 suanlinfang.cn/translate?from=cnblogs

posted @ 2026-10-13 09:40  吴键WJ  阅读(14)  评论(0)    收藏  举报