分布式事务一些思考和总结

最近在做交易订单的事情,随着系统越来越复杂,交易量越来越大,出现不一致的情况也频发。不禁引人思考,这里做一些总结记录。分布式事务有一些理论,最常听的是CAP,ACID等。这里就不做介绍了,大家自己去查资料看看。分布式事务也经常提到2PC,3PC,因为不太适用互联网场景,这里也不做介绍。

下面我们来看看一个电商典型的场景,下单后发送消息给其他业务系统,很多同学喜欢这样做

    @Transactional
    public void submitOrder(){
        insertDb();
        sendMsg();
    }

用Spring注解来实现事务,首先先插数据到DB,然后发消息。仔细分析遇到的各种情况:

  • 1.插入数据和发送消息都成功,这是最常见的情况,当然是皆大欢喜。
  • 2.插入数据失败,抛出异常,消息当然也不会发,这也没什么问题。
  • 3.插入数据成功,只是在提交commit的时候由于网络原因连接超时,抛出异常,回滚无效,消息仍旧发送成功。算是有惊无险。
  • 4.事务在commit的时候失败回滚,消息已经发送,造成消息多发。
  • 5.消息发送失败,事务回滚,没什么问题。
  • 6.消息发送成功,网络原因连接超时,事务回滚,造成消息多发。
  • 7.事务提交的时候,机器宕机,也有可能造成消息多发。

分析以上几种情况,除了消息多发,也没什么大问题,消息订阅者可以反查一下订单,如果查不到可以丢弃该消息,但是会对业务方造成一定的困惑。

但是如果把sendMsg替换一个服务,情况就不同了。假设取消订单需要先更新订单状态,然后回冲库存。

   @Transactional
    public void cancelOrder(){
        updateDB();
        inventoryService.rollback();
    }

从之前的案例分析来看,这样会造成订单状态更新失败的情况下,调用库存接口成功,显然出现了不一致情况。无论怎么样调整操作顺序,都会出现类似问题。那么怎么样尽量规避这种问题发生?

本地事务表

核心思路是将分布式事务转换为本地事务。新建message表,保存业务ID和自身状态。更新订单操作和插入message在同一个库中做事务,能保证一致性。

   @Transactional
    public void cancelOrder(){
        updateDB();
        insertMessage();
    }

message表

idstatus
orderId未处理

如果上述事务成功后了,接下来发送消息给库存服务或者直接调用库存服务,成功后更新消息表状态。这里有个问题,如果下来操作失败怎么办?这时候message表就派上用场了,需要另外一个务定时扫描message表,将没有处理发送的消息再次发送出去。库存服务还需要保持幂等性。

外置事务表

有些服务本身不开启本地事务,而是调用其他RPC服务,例如下面接口调用了serviceA,serviceB,serviceC接口。怎么保证这3个接口要么都调用成功?其实没有确切答案。只能借鉴前面的思路,设计一个外部事务表。每次调用前预先生成全局序列号id,记录这次事务需要调用哪些接口。每次调用成功后更新状态为成功。当然还是存在多次调用的情况,被调用方需要做好幂等防重措施。这里还有很多需要考虑的地方,一个是性能优化,还有就是完善失败后补偿机制,

全局序列号服务调用服务顺序状态
123doSomethingserviceA1OK
123doSomethingserviceB2OK
123doSomethingserviceC3OK
public void doSomething(){
    serviceA();
    serviceB();
    serviceC();
}

事务消息

Rocketmq有类似实现。
首先先发送prepare消息给Rocketmq,这时消息并不发送。等事务提交后,发送确认消息给Rocketmq,消息再发送出去。如果Rocketmq没有收到确认消息,会询问业务方是否要发送。
这里写图片描述

数据库日志

通过数据库日志,比如监听mysql的binlog日志,通过canal等中间件把数据变更日志推送出去,然后再调用后续服务,也是可行方案。

其他方案

支付宝TCC,这里转发一张图片,有兴趣的自己查阅资料
这里写图片描述

最后屏障

自动对账,人工介入

总结

实现分布式事务一些思路

  • 一定有一个协调者,在事务的不同阶段介入,保证事务的最终一致性。这个协调者是一个高度抽象,它可能是一个中间件,可能是一个服务,甚至是一个任务,不管怎么说,它一定是各个事务单元的第三者,协调各个事务单元最终达成一致性。
  • 协调者必须知道分布式事务的全貌,知道了发生了什么,没发生什么,该发生什么。
  • 事务发生前结果应该是预知的,或者说事务发生后结果是确定的。这就需要协调者能处理各种情况发生,不能遗漏任何情况,知道各种情况下的补偿策略,或回滚,或重试,或者尽最大努力送达,或者通知人工介入。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值