vlambda博客
学习文章列表

分布式事务—柔性事务解决方案之本地消息表模式

  • 推荐好文:


  • 1、引入经典问题

    • 1.1 单体架构

      • (1)架构图

      • (2)库表设计

      • (3)优缺点

    • 1.2 分布式架构

      • (1)架构图

      • (2)优缺点

  • 2、分析问题

  • 3、解决问题—本地消息表模式

    • (1)库表设计

    • (2) 步骤

    • (3)架构图

    • (4)分析

  • 4、总结

1、引入经典问题

场景如下:
以大家喜闻乐见的例子——订单系统为例,用户下单后,订单相应的商品库存进行扣减,步骤如下:
1:用户下单成功
2:扣减商品库存
3:生成出库单
4:更新订单的状态为待出库

1.1 单体架构

(1)架构图

(2)库表设计

订单表:
分布式事务—柔性事务解决方案之本地消息表模式
库存表
分布式事务—柔性事务解决方案之本地消息表模式
出库单表:
分布式事务—柔性事务解决方案之本地消息表模式

(3)优缺点

单体架构,比较原始的架构。将所有功能都部署在一个web
容器中运行的系统。项目打包后,所有服务都在同一个war包中,部署在一
个web容器中,共用一个数据库,本地事务很容易保证。
1.优点
1.容易测试
2.容易部署
3.本地事务强一致性,保证ACID
2.缺点
①随着系统业务量的增加,系统过于庞大和复杂,代码都耦合在一起,代码可读性差,维护起来比较难。
②因为应用太大,每启动一次都需要很长的时间,因此从编辑到构建、运行再到测试这个周期花费的时间越来越长,开发效率低。
③后续增加新的业务,不能做到按需扩展,只能扩展整个系统,扩展性不高。
④会因为一个模块的错误导致整个系统宕机,稳定性不高。
⑤部署不灵活,修改了某个模块的代码,需要将整个系统重新构建部署。
⑥随着系统用户量的增加,用户高并发访问数量有限。
所以现在大部分企业的技术架构都是以分布式微服务作为基础。
对分布式和微服务的认识可参考该博客:
详解单体架构 微服务 微服务架构 微服务各个组件 分布式 集群 负载均衡

1.2 分布式架构

(1)架构图

分布式事务—柔性事务解决方案之本地消息表模式

(2)优缺点

1.优点
1.单一职责,逻辑清晰
2.简化部署
3.灵活扩展
4.技术异构
5.高可靠
2.缺点
1.复杂度高
2.运维复杂
3.分布式事务不容易保证
等等。。。
对分布式和微服务的认识可关注CSND王德印的博客:
详解单体架构 微服务 微服务架构 微服务各个组件 分布式 集群 负载均衡

2、分析问题

1.因为分布式系统,必然导致跨库事务的问题,简单说就是一个应用某个功能需要操作多个库,不同的库中存储不同的业务数据,意味着Spring原生的事务机制就无法保证ACID。所以我们要解决的问题就是分布式事务问题。

2.当然分布式事务解决方案有很多种,XA模式,2PC模式,3PC模式,TCC模式,可靠性最终一致性解决方案等等。

3.那么该业务场景使用哪种方式最好呢,很显然该场景不需要实时的,所以适用于CAP原则中AP,使用最终一致性是最合适的,所以本文讲的是可靠消息最终一致性解决方案之本地消息表模式。

3、解决问题—本地消息表模式

本地消息表模式的核心通过本地事务保证数据业务操作和消息的一致性,然后通过定时任务发送给消费方或者中间加一层MQ的方式,保障数据最终一致性。

(1)库表设计

订单微服务中出库本地消息表:
分布式事务—柔性事务解决方案之本地消息表模式

(2) 步骤

基础功能
1:用户下单,订单微服务创建订单,插入订单表

2:同时插入本地消息表(订单的出库单本地消息表),并根据订单号设置消息表为待确认状态

3:同步/异步调用库存微服务接口,扣减商品库存,并插入出库单。如果为了提高性能和用户体验,这里可以使用异步方案,使用线程或者MQ。下篇更新加入MQ的本地消息表模式。

4:同步/异步调用订单微服务接口,更新消息表为确认状态、更新订单状态为待出货状态

定时任务的作用:
1:首先task微服务开启一个定时任务,定时去轮询订单微服务中的本地消息表中未确认的消息数据;

2:然后调用库存微服务中的接口,根据订单编号(本地消息表和出库单表都有订单编号),判断本地消息表中的订单是否存在于出库单中;

3:如果本地消息表中的订单在出库单中不存在,则调用库存微服务中的接口,更新商品库存,并插入出库单,在同一事务中操作;

4:然后根据订单号更新本地消息表中的状态为已确认;如果更新消息失败,继续执行第一步操作;

5:如果本地消息表中的订单在出库单中存在,则将消息状态更新为已确认;

6:更新订单状态为待发货。

(3)架构图

分布式事务—柔性事务解决方案之本地消息表模式
Task微服务的任务:

(4)分析


情况1:理想情况下,所有操作都成功,定时任务空轮询,数据一致性没问题。

情况2:更新消息表和更新订单状态失败,这个时候本地消息是待确认状态,task微服务定时轮询待确认的订单,然后根据订单号查询已存在的出库单(保证接口幂等性),所以不再进行扣减库存,而是直接更新消息表为已确认和更新订单状态为待发货,如果更新消息表和更新订单状态失败了,通过定时任务轮询保证数据最终一致。

情况3:库存扣减失败,这个时候本地消息是待确认状态,task微服务定时轮询待确认的订单,然后根据订单号查询出库单(保证接口幂等性),出库单不存在,所以先进行扣减库存,然后更新本地消息表为已确认和更新订单状态为待发货,如果更新消息表和更新订单状态失败了,通过定时任务轮询待确认的订单,保证数据最终一致。

情况4:业务层面库存不足扣减失败,这时候直接更新订单微服务的本地消息表状态为已作废,通知用户下单失败,如果更新订单微服务的本地消息表状态失败,通过定时任务轮询待确认的订单,保证数据最终一致性。

情况5:订单微服务插入本地消息表失败,创建订单和插入消息表在同一事务中,由本地事务保证数据一致性。

情况6:就很明显了,不用多说。

4、总结

本文讲解了可靠消息最终一致性解决方案之本地消息表模式,解决分布式事务的方案,这也是很常用的是一种方式。

如果并发量不是特别大,该方案足够适用了,如果系统并发量比较高,可以适用MQ进行优化提高性能和吞吐量。

使用MQ的话,就多了一层中间件,引入中间件势必会增加系统的复杂度,出错的概率也会增大,当然引入MQ的话也会提高相应的性能等优势,下期出本地消息表引入MQ的模式~并解决MQ的消息可靠性问题,消息堆积问题,重复投递和重复消费等问题。

推荐好文: