背景
- 互联网行业为了性能和实现成本考虑,通常追求最终一致性
- 对于高速发展的业务,或者依赖历史遗留系统,seata一类的方案有较多限制:
- 发起方和依赖方需要实现一套协议,业务上不接受过高的改造成本或者无法改造:如AT的RM等接口,TCC的try,commit,cancel
- 事务状态流转较复杂,接:如seata涉及分支事务和十几种状态机
想法
业界方案的关注点
- 业界的比较优秀的框架追求的是一种大而全的解决方案,并没有太多考虑到业务上的对于技术成本的预期
降低成本的措施
- 对限制1(依赖):依赖方只提供一对正向+逆向的操作能力,发起方自身兼容。无需依赖方实现特定接口。
- 对限制2(复杂):问题分解
- 流程分解:
1. 业务流程分为多个子流程,如交易流程分为下单、支付、退款等,每个子流程保证自身一致性。无需使用跨整个交易流程的事务。
2. 子流程将自身的步骤分类、排序,归入统一的解决方案。 - 依赖分层:
1. 发起方保证发起方自身和下一层依赖的一致性,下一层依赖保证第二层依赖的一致性。无需事务传播、分支事务,减小问题复杂度。
依赖分层方案
流程分解方案
- 分解原则:按照saga理论,将步骤分为
- 只读步骤。不修改数据,如:下单校验。
- 可补偿步骤。可能失败,需要编写补偿事务做回滚,如:下单预扣库存。
- 关键步骤。后面跟着不可能失败(有明确结果)的步骤,如:创建订单,同时本地事务里发领域事件。
- 可重复步骤。总会成功或者弱依赖,如:支付回调实扣库存、风控同步。
同步链路
- 触发:用户触发
- 目标:针对可补偿事务和关键性事务,依赖回滚、兜底达到最终一致
- 关键点:
- 如何判断事务状态
- 如何保证最终成功提交、回滚
- 思路:
- 事务表持久化全局事务状态,只包含三个状态:开启(不一致)、提交完成(一致)、回滚完成(一致)。如果事务处于开启状态,根据关键步骤是否成功判断事务实际的状态,从而执行提交或者回滚。
- 消息重试、任务兜底
- 同步回滚失败,消息异步回滚,依赖方全部回滚成功后,全局事务回滚完成
- 消息回滚失败则依赖兜底任务补偿回滚
异步链路
- 触发:系统
- 目标:针对可重复事务,依赖可靠消息达到最终一致
- 关键点:如何保证消息的可靠性,即不重、不漏、不堵
- 思路:
- 不重:寻找幂等做幂等,一般异步链路自身也会幂等。
- 不漏:依赖消息中间件可靠性和异构通道,先执行业务逻辑,再ACK。
- 不堵:异构通道,可以是另一个集群的消息(快速),也可以是兜底任务(可靠)
延迟异步链路
- 特殊的异步链路:
- 延迟时间较短,比如一个周内。同上。
- 延迟时间过长,比如一年,延迟消息无法支持。需要依赖延迟调度任务。
核心场景
场景 | 发起方 | 依赖方操作 | 一致性方案 |
---|---|---|---|
下单 | 用户 | 预扣库存、优惠等资源,创建订单 | 同步链路方案 |
支付 | 用户 | 创建支付单,修改订单状态 | 同步链路方案 |
支付回调 | 支付系统 | 修改订单状态,实扣库存、优惠等资源,触发履约 | 异步链路方案 |
退款 | 用户、客服 | 创建退款单,送审 | 同步+异步链路方案 |
审核回调 | 商家 | 退款,修改退款单状态 | 异步链路方案 |