分布式任务为什么必须做幂等?如何保证任务幂等?
简化版
分布式任务必须做幂等,因为任务可能因超时、重试、调度中心故障、执行器重启等原因被重复触发。保证幂等的常见方式有唯一约束、业务状态机、去重表、执行流水号、乐观锁和先查后写。
详细版
任务调度系统很难保证业务层绝对只执行一次。即使调度中心只触发一次,也可能出现执行成功但回调失败,调度中心误以为失败后重试。
幂等设计方法:
- 唯一约束:例如
user_id + coupon_id + activity_id防止重复发券。 - 状态机:只有特定状态才能流转,已处理状态直接跳过。
- 去重表:记录
task_id + biz_id,重复插入失败则跳过。 - 业务流水号:同一业务请求只处理一次。
- 乐观锁:更新时带版本号或状态条件。
- 分布式锁:用于减少并发重复,但不能替代幂等。
面试重点:锁只能降低重复并发概率,幂等才是重复执行后的正确性保障。
完整版教学
一、为什么任务会重复执行
很多人以为“调度中心控制一下,不重复调度就行”。现实里重复执行非常常见。
典型场景包括:
- 执行器已经执行成功,但回调调度中心时网络超时。
- 调度中心没有收到成功结果,于是触发失败重试。
- 执行器执行到一半重启,任务被其他节点接管。
- 运维人员看到任务失败,手动点了重跑。
- 分片任务重新分配时,某个分片被再次执行。
这些场景都不是异常设计,而是分布式系统的常态。任务幂等就是为了接受这种不确定性。
二、什么叫任务幂等
幂等的意思是:同一个业务动作执行一次和执行多次,最终结果一致。
比如“给用户发一张活动券”,重复执行不能变成发两张。正确结果应该是:第一次发成功,第二次发现已发过,直接跳过或返回成功。
伪代码可以这样写:
if exists_coupon(user_id, activity_id):
return success
insert coupon(user_id, activity_id)
但仅靠先查后写在并发下不安全,最好加数据库唯一约束:
unique(user_id, activity_id)
这样即使两个任务同时执行,也只有一个能插入成功。
三、状态机如何保证幂等
很多任务适合用状态机控制。例如超时关闭订单:
待支付 -> 已关闭
已支付 -> 不允许关闭
已关闭 -> 重复关闭直接跳过
SQL 可以带状态条件:
update orders
set status = 'CLOSED'
where id = ? and status = 'WAIT_PAY';
如果影响行数是 1,说明本次关闭成功;如果是 0,说明订单可能已支付或已关闭,任务不应继续重复处理副作用。
四、去重表适合什么场景
去重表适合外部副作用明显、需要记录处理流水的任务。例如同步第三方账单、发送通知、发放奖励。
可以设计:
task_name
biz_id
execute_date
status
执行前先插入去重记录,唯一键是 task_name + biz_id + execute_date。插入成功才处理,插入失败说明已经处理过或正在处理。
这种方式的好处是可追踪,坏处是表可能很大,需要归档和清理。
五、分布式锁为什么不能替代幂等
分布式锁只能让同一时刻尽量只有一个执行者,但不能覆盖所有异常。
例如任务拿到锁后执行成功,释放锁前进程崩溃;或者锁过期后另一个节点进来执行;或者人工重跑任务。只靠锁无法证明业务没有重复产生副作用。
更稳的设计是:锁用于减少并发冲突,幂等用于保证重复发生后结果仍正确。
六、常见误区与追问
这道题要紧扣「任务幂等」本身回答,不能把它混成泛泛的分布式任务调度套话。面试官通常会沿着“为什么需要、流程怎么走、失败怎么兜、代价是什么”继续追问,所以回答要覆盖调度触发、分片分配、抢占锁、失败重试、超时控制、幂等和高可用。
| 回答层次 | 要讲清的内容 | 容易漏掉的边界 |
|---|---|---|
| 核心结论 | 任务幂等保证同一任务因重试、抢占或重复触发执行多次时,业务结果仍只生效一次 | 不要停在名词解释 |
| 流程机制 | 生成任务唯一键 -> 检查处理状态 -> 加锁或乐观更新 -> 执行业务动作 -> 记录成功结果 -> 重复请求直接返回 | 要说清触发点、状态变化、确认点和失败兜底 |
| 工程取舍 | 扣款任务如果重试 3 次,必须通过业务唯一键或状态机保证只扣一次 | 任务调度提升自动化和吞吐,但必须处理重复执行、漏执行、积压、超时和故障恢复 |
任务幂等 面试拆解:
1. 生成任务唯一键
2. 检查处理状态
3. 加锁或乐观更新
4. 执行业务动作
5. 记录成功结果
6. 重复请求直接返回
记忆钩子:先给结论,再拆流程,再讲数字例子和失败边界;回答「任务幂等」时要围绕题目问法收束,不要把相邻概念堆成一段没有重点的名词清单。
- 误区:调度框架能完全避免重复执行。 网络抖动、超时重试和故障切换都可能导致重复,业务必须幂等。
- 误区:只靠分布式锁就够了。 锁过期、释放失败或任务超时仍可能重复,最终要靠业务状态兜底。
- 误区:读任务不需要幂等。 读任务若会写进度、发通知或导出文件,也可能产生副作用。
- 追问:常见幂等手段? 唯一键、状态机、去重表、乐观锁、版本号和幂等 token。
- 追问:任务超时后如何处理? 不要盲目重跑,先判断上次是否已部分成功,再补偿。
- 追问:如何设计去重表? 用业务唯一键记录执行状态和结果,重复执行先查表。
七、加强记忆
任务调度一定要默认“可能重复执行”。调度系统负责尽量少重复,业务幂等负责重复后不出错;唯一约束、状态机、去重表、流水号和乐观锁,是任务幂等最常用的护城河。