两个冲突的长生命周期进程管理器(saga)并发执行问题解决方案咨询
问题核心根因
该问题的本质是单条目标资源的跨进程操作缺乏原子性和顺序保障,在存在第三方操作方的场景下,单纯重试REMOVE命令无法区分「Add Saga尚未执行对应条目的添加操作导致的移除失败」和「第三方已经提前执行过移除操作导致的失败」,因此会出现最终结果随机的情况。
可落地的解决方案
方案1:单条目加版本戳实现操作优先级管控
- 给每个待操作条目新增2个元数据字段:
op_version(当前条目操作版本号,初始值为0)、last_op_type(最近一次成功执行的操作类型:ADD/REMOVE) - 维护一套全局单调递增的版本号生成规则,启动两个Saga之前先申请两个连续的版本号:Add Saga绑定版本号N,Remove Saga绑定版本号N+1,第三方发起的所有ADD/REMOVE操作也需要申请独立的新版本号
- 所有操作的执行逻辑统一修改为乐观锁模式:
- 先查询目标条目当前的
op_version - 仅当请求携带的版本号 > 条目当前版本号时,才执行对应的ADD/REMOVE操作,执行成功后将条目
op_version更新为请求版本号 - 如果请求版本号 ≤ 当前版本号,直接返回成功/失败,无需重试
- 先查询目标条目当前的
- 该方案下不管操作执行顺序如何,最终所有条目都会以最高版本的操作结果为准,千万级数据新增字段的存储和查询开销极低,不会影响原有执行性能
方案2:单条目操作串行化
- 对同一个条目的所有ADD/REMOVE操作强制按提交顺序执行,完全避免竞态,常见实现方式:
- 按条目ID做哈希分片,每个分片对应一个独立的消息队列,所有同ID的操作都投递到对应队列,单线程消费执行
- 操作执行前先对条目ID加分布式锁,只有持有锁的进程可以执行操作,执行完成后释放锁
- 该方案逻辑简单易维护,只要所有操作方都遵守串行化规则,即可兼容任意第三方的操作请求
方案3:幂等改造 + 最终一致性兜底校验
- 先对ADD和REMOVE命令做幂等改造:重复执行同一个ADD命令不会重复追加内容,重复执行同一个REMOVE命令即便目标内容不存在也返回成功
- 两个Saga执行完成后,启动低优先级的后台校验任务,遍历所有千万级条目,检查最终状态是否符合预期,对不符合预期的条目补执行对应操作即可
- 该方案开发成本最低,适合对实时性要求不高、允许短时间状态不一致的场景
方案选型建议
- 实时性要求高、第三方操作较多的场景:优先选版本戳方案,性能损耗极小,适配所有业务场景
- 已经有成熟的分布式锁/分片消息队列基础设施的场景:可选单条目串行化方案,维护成本低
- 对一致性时效性要求不高的场景:选最终一致性校验方案,落地速度最快
内容的提问来源于stack exchange,提问作者ayeo
相关产品推荐
相关产品推荐

