其他零散知识点Java 程序员分布式吃瓜

分布式事务实战方案汇总

2021-03-02  本文已影响0人  程序花生

1. 最终一致性

1.1 本地事务表 + 轮询补偿

交互流程

场景:重构业务新老系统双写库同步

项目背景

这是一个重构系统新老系统同时服役切量迁移的业务场景,老系统正在线上服役为各业务方提供接口查询功能,新系统重构完成后需要对接接入,调用流量要陆续从老系统切换到新系统,最终老系统迭代下线。

分布式事务

需要解决的分布式事务问题就是,双系统的数据是异构、分散的,线上不可停量,需要陆续切换完成,因此需要事先将老库存量数据洗入新库,此过程中增量数据写入是存在的,但是最终新老库是相对一致和统一的,该场景需要解决的是数据双库的双写问题

设计方案

场景Q&A

场景:第三方认证核验

项目背景

这是一个认证系统以来外部核验系统进行用户身份鉴权的场景,即认证系统记录认证发起记录,并请求到外部的核验系统发起一笔核验请求,用户在核验系统确认后核验结果返回到认证系统确认用户的真实数据状态。

分布式事务

该流程中认证系统是一个本地系统,存放用户发起的认证流水信息和核验状态,依赖外部核验系统返回该笔认证记录的核验状态,由于核验过程是异步的,用户可以选择任意时间完成或者永远不完成,需要保证每次认证流程只有一笔业务发起,而且需要根据业务时间进行核验流程的超时进行强制取消或者补偿查询对齐核验状态等,需要解决的分布式事务是认证流水、核验结果的一致性

设计方案

场景Q&A

1.2 本地事务表 + 事务消息

交互流程

场景:分库分表路由字段绑定

项目背景

该业务是在分库分表场景下,需要一个切分键字段进行数据分片路由,一般我们ToC业务的话会使用userId这样的字段进行切分。然而水平切分数据带来了数据库读写性能的同时也存在一个问题,那就是查询必须携带切分键才可以完成,因为要依赖它进行数据路由查询,比如在以userId进行数据路由切分时,如果想按照用户的身份证、姓名等进行匹配查询是无法实现的,因为我们事先是不知道userId、身份证、姓名的等值匹配关系。一般而言,我们可以通过HBase + ES的方案进行解决,即监听不同库的binlog日志,将其按照时间进行排序切分汇入HBase表中,加入要检索的索引到ES中解决分库分表下数据切片产生的聚合问题。

分布式事务

基于以上场景,除了通过HBase+ES实现之外,还可以通过切分键与非切分键进行数据绑定解决,但是由于切分键与非切分键的路由一般不一致,数据会分散到不同库,因此无法做本地事务,这是我们需要解决的分布式事务问题。

设计方案

场景Q&A

1.3 TCC(Try-Commit-Cancel)

交互流程

TCC事务其实主要包含两个阶段:Try阶段、Confirm/Cancel阶段。

从TCC的逻辑模型上我们可以看到,TCC的核心思想是,Try阶段检查并预留资源,确保在Confirm阶段有资源可用,这样可以最大程度的确保Confirm阶段能够执行成功。这里的资源可以是DB,或者MQ,RPC,只要是分布式环境中的事务载体就可以,而且需要这些分布式事务的参与者具备正逆向条件,比如DB、MQ的事务可以支持2PC,可以根据协调者对其进行事务提交或者取消操作,RPC比如账户服务可以支持正向增加也可以支持逆向减少,除此之外,DB、MQ要自身支持事务的ACID,这是参与分布式事务的原子性保证,RPC底层也是DB,这里可以等同理解。以上参与者具备参与分布式事务的基本条件后便可以进行整合和使用,业务流程的驱动和事务的完整性由中间协调者来操作和推进。

场景:积分商品兑换

项目背景

这是一个电商系统比较经典的下单、扣款、发货流程,在下单之前会通过一系列商品在架状态、库存数量、购买限制等有效性过滤,然后在业务系统中进行一个商品兑换的场景。

分布式事务

由于是商品兑换,对于用户和系统本身来说是这个过程一个原子性的、一键完成的操作,因此下单+动账+发货是一个现实存在的分布式事务。这里假设订单数据和动账数据在一个本地数据库事务中,持久化开启数据库本地事务,该事务中记录订单生成数据和动账数据信息,以及发货状态的信息记录。这里需要解决的分布式事务是订单数据、动账数据、发货状态三种的最终一致

设计方案

场景Q&A

场景:广告任务结算

项目背景

当一个App有了足够多的用户体量,便开始有商家进行广告或商品的投放,平台通过包装运营活动、广告位等,将用户曝光与商家付费结合达到流量变现的目的。

分布式事务

当用户进行浏览、关注、商品购买、视频观看、App下载等多种任务,我们会根据商家配置等付费规则进行商家广告费用的扣减,同时还要为用户完成任务进行奖励结算,此时的分布式事务便是商家账户扣减与用户账户增加的数据一致性问题

设计方案

场景Q&A

场景:运营活动抽奖

项目背景

抽奖是运营活动中比较常见的方式,对于用户来说需要做的是参加设定人物获取积分,攒够积分就可以开始抽奖,抽中后即等待奖品入账,一般券会立刻入账使用,而实物商品则需要耐心等待物流送到用户手上。

分布式事务

关于抽奖,涉及账户动账、库存参与次数扣减、抽奖等多个环节,该场景要解决的分布式事务是账户动账、活动库存变更、抽奖下单数据一致性

设计方案

场景Q&A

2. 弱一致性

2.1 最大努力通知 + 消息重试控制

场景:数据变更同步下游业务方

项目背景

系统数据发生变更时,会对外部系统产生一定影响,外部系统需要知道这种数据变化,这便是数据状态同步的场景。一般来说数据交互可以有推(Push)、拉(Pull) 两种形式,这里先说推模式,即数据变更方负责将变化通知到数据关注方。

分布式事务

这里要保证的是数据变更在多个应用中的状态一致

设计方案

场景Q&A

这里是弱一致性的实现,没有做本地事务表和定时任务轮询对比各事务状态进行补偿操作。完全依赖于MQ的失败重试驱动,若RPC调用失败即数据同步业务方失败,MQ会一直进行重试操作,随着重试次数增加,重试间隔也会增加,这里也可以业务自行进行最大努力尝试次数的控制,超过多少次尝试仍失败则放弃,因此不能保证最终一致

场景:数据变更广播下游业务方

项目背景

这里是数据同步的说拉模式,即数据关注方对数据变更方进行数据状态变更的监听,这种处理方式处理的主动权在于数据关注方,数据变更方只负责和保证一定通知到数据变更情况,是否能够同步成功则由监听方处理和兼容。

分布式事务

这里要保证的是数据变更在多个应用中的状态一致

设计方案

场景Q&A

这里也是弱一致性的实现,没有做本地事务表和定时任务轮询对比各事务状态进行补偿操作。完全依赖于MQ消费方的处理,若消费方处理失败或在消息队列规定时间内没有消费完毕,则数据无法保证最终一致

2.2 战略放弃 + 报警 + 人工修复

场景:秒杀库存回滚

项目背景

在秒杀场景中,最复杂的除了解决高并发问题外,最核心的就是活动商品的库存控制、变更问题,一般商品库存会初始化到Redis缓存中进行管理,秒杀方法会对Redis缓存库存数量进行校验、扣减操作,通过MQ异步扣减DB库存,既利用Reids原子操作进行库存数量操作,又利用缓存抗住高并发请求,起到异步削峰的作用,这是秒杀的正向流程。而逆向流程是用户秒杀到商品预占了库存,但是没有及时进行订单支付或者进行了订单取消,此时要发起对库存的恢复操作。

分布式事务

这里的分布式事务是Redis缓存库存与DB库存数量一致性问题

设计方案

场景Q&A

这里也是一个弱一致性的实现,业务场景我们保证不超卖即可,对于极端情况出现的库存数量无效多扣减做战略性放弃,一般情况下不会影响大多业务使用,如果非要吹毛求疵达到账户金额那种强一致性,思路也很简单,可以借助定时任务轮询对比缓存与DB库存数量进行校验,这里还要考虑到其他在行流程如超市关单库存恢复,仍然在行的秒杀活动等,保证数据处理不多加不多减。

3. 总结

3.1 分布式角色

3.2 技术保证

3.2 强弱一致选择

3.3 幂等&防重

3.4 尽早干预&补偿一致

指的是代码逻辑上尽早对串行处理的做个子事务进行回滚或逆向操作,这样可以尽快结束分布式事务,而不需要等待相对更为延迟的定时任务或其他补偿机制来驱动,这里可以使用旁路方法或不阻塞主方法放到MQ或异步线程中进行处理,比如秒杀下单发货因为库存不足或商品下架可以立刻进行发起关单退款的逆向流程

补偿机制一般可以通过定时任务、MQ重试来进行子事务驱动整个分布式事务的完结

作者:阿里山小火车
链接:https://juejin.cn/post/6934911674969227295
来源:掘金

上一篇 下一篇

猜你喜欢

热点阅读