其他方案
1.事务状态表+调⽤⽅重试+接收⽅幂等
介绍
调⽤⽅维护⼀张事务状态表(或者说事务⽇志、⽇志流⽔),在每次调⽤之前,落盘⼀条事务流⽔,⽣成⼀个全局的事务ID
事务开始之前的状态是Begin,全部结束之后的状态是End。如果某个事务⼀直停留在Begin状态,则说明该事务没有执⾏完毕
然后有⼀个后台任务,扫描状态表,在过了某段时间后(假设1次事务执⾏成功通常最多花费30s),状态没有变为最终的状态,说明这条事务没有执⾏成功。于是重新调⽤系统的接口。保证这条流⽔的最终状态是End状态
事务状态表
补充说明
- 幂等问题:系统调用方和被调用方根据全局的事务ID做幂等操作,所以即使重复调⽤也没有关系
- 如果后台任务重试多次仍然不能成功,要为状态表加⼀个Error状态,通过⼈⼯介⼊⼲预
- 对于同步调⽤,调⽤⽅调⽤A或B失败的时候,可以重试三次。如果重试三次还不成功,则放弃操作,再交由后台任务后续处理
2.对账
介绍
所有的“过程”都必然产⽣“结果”,过程是我们所说的“事务”,结果就是业务数据。⼀个过程如果部分执⾏成功、部分执⾏失败,则意味着结果是不完整的。从结果也可以反推出过程出了问题,从⽽对数据进⾏修补,这就是“对账”的思路
分类
- 全量对账:⽐如每天晚上运作⼀个定时任务,对比两个数据库
- 增量对账:可以是⼀个定时任务,基于数据库的更新时间;也可以基于消息中间件,每⼀次业务操作都抛出⼀个消息到消息中间件,然后由⼀个消费者消费这条消息,对两个数据库中的数据进⾏⽐对(当然,消息可能丢失,⽆法百分之百地保证,还是需要全量对账来兜底)
例子
案例1
电商⽹站的订单履约系统。⼀张订单从“已⽀付”,到“下发给仓库”,到“出仓完成”。假定从“已⽀付”到“下发给仓库”最多⽤1个⼩时;从“下发给仓库”到“出仓完成”最多⽤8个⼩时。意味着只要发现1个订单的状态过了1个⼩时之后还处于“已⽀付”状态,就认为订单下发没有成功,需要重新下发,也就是“重试”。同样,只要发现订单过了8个⼩时还未出仓,这时可能会发出报警,仓库的作业系统是否出了问题……诸如此类
案例2
微博的关注关系。需要存两张表,⼀张是关注表,⼀张是粉丝表,这两张表各⾃都是分库分表的。假设A关注了B,需要先以A为主键进⾏分库,存⼊关注表;再以B为主键进⾏分库,存⼊粉丝表。也就是说,⼀次业务操作,要向两个数据库中写⼊两条数据,如何保证原⼦性?
案例3
电商的订单系统也是分库分表的。订单通常有两个常⽤的查询维度,⼀个是买家,⼀个是卖家。如果按买家分库,按卖家查询就不好做;如果按卖家分库,按买家查询就不好做。这种通常会把订单数据冗余⼀份,按买家进⾏分库分表存⼀份,按卖家再分库分表存⼀份。和案例2存在同样的问题:⼀个订单要向两个数据库中写⼊两条数据,如何保证原⼦性?
案例2和3的解决思路
如果把案例 2、案例 3 的问题看作为⼀个分布式事务的话,可以⽤对账解决
因为两个库的数据是冗余的,可以先保证⼀个库的数据是准确的,以该库为基准校对另外⼀个库
总结
对账的关键是要找出“数据背后的数学规律”。有些规律⽐较直接,谁都能看出来,⽐如案例2、案例3的冗余数据库;有些规律隐含⼀些,⽐如案例1的订单履约的状态。找到了规律就可以基于规律进⾏数据的⽐对,发现问题,然后补偿
3.弱⼀致性+基于状态的补偿
场景
需求解析
对于该需求,有⼀个关键特性:对于电商的购物来讲,允许少卖,但不能超卖。⽐如有100件东西,卖给99个⼈,有1件没有卖出去,这是可以接受的;但如果卖给了101个⼈,其中1个⼈拿不到货,平台违约,这就不能接受。⽽该处就利⽤了这个特性
流程
- 提交订单成功,扣库存成功,返回成功
- 提交订单成功,扣库存失败,返回失败,调⽤⽅重试(此处可能会多扣库存)
- 提交订单失败,不再扣库存,调⽤⽅重试
解决
只要最终保证库存可以多扣,不能少扣即可
但是,库存多扣了,数据不⼀致,怎么补偿呢?
库存每扣⼀次,都会⽣成⼀条流⽔记录。这条记录的初始状态是“占⽤”,等订单⽀付成功后,会把状态改成“释放”
对于那些过了很长时间⼀直是占⽤,⽽不释放的库存,要么是因为前⾯多扣造成的,要么是因为⽤户下了单但没有⽀付
通过⽐对,得到库存系统的“占⽤又没有释放的库存流⽔”与订单系统的未⽀付的订单,就可以回收这些库存,同时把对应的订单取消。类似12306⽹站,过⼀定时间不⽀付,订单会取消,将库存释放
4.重试+回滚+报警+⼈⼯修复
先扣库存,后创建订单。不做状态补偿,为库存系统提供⼀个回滚接口。创建订单如果失败了,先重试。如果重试还不成功,则回滚库存的扣减。如回滚也失败,则发报警,进⾏⼈⼯⼲预修复
总之,根据业务逻辑,通过三次重试或回滚的⽅法,最⼤限度地保证⼀致。实在不⼀致,就发报警,让⼈⼯⼲预。只要⽇志流⽔记录得完整,⼈⼯肯定可以修复!通常只要业务逻辑本⾝没问题,重试、回滚之后还失败的概率会⽐较低,所以这种办法虽然丑陋,但很实⽤