分布式事务

分布式事务的解决方案

XA协议是基于数据库的分布式事务协议。他有两个部分:事务管理器和本地资源管理器。
事务管理器是全局调度者,命令各个资源管理器进行提交或回滚。衍生的有二阶提交协议2PC和三阶提交协议3PC。

2PC

在这里插入图片描述

协调者通知所有的参与者要开始提交,各个参与者进行业务操作,操作完毕后返回给协调者是否成功,协调者如果接收到了某个参与者的失败消息,就发送给所有参与者回滚Rollback命令。如果都成功了,就发送给所有参与者Commit命令。

缺点

  • 因为网络抖动而导致数据不一致:部分提交和部分未提交,由于网络抖动,有些参与者接收到了Commit命令,有些没有接受到
  • 超时导致的阻塞问题:当一个参与者发生通信超时,其他的参与者也就只有占用资源,不进行释放,导致阻塞
  • 单点故障的风险:依赖协调者,如果协调者发生故障,此时其他参与者处于锁定状态,无法完成Commit操作。虽然故障后会选举出一个新的协调者,可是无法解决阻塞问题。

3PC

在这里插入图片描述

  • 这里多加了一步PreCommit,保证了在最后提交之前,各参与者状态一致。
  • 同时,给参与者和协调者都引入了超时机制,参与者超过一定时间没有接收到Commit命令,就会自行Commit或者Rollback,协调者超过一定时间没有接收到响应,也会给所有的参与者发送Rollback命令。
  • 解决了超时阻塞和单点故障问题。但是仍然没有解决因为网络抖动导致的数据不一致问题。

一共分三步:

  • CanCommit:协调者询问参与者是否可以提交事务,这时参与者执行业务,并返回是否可以提交
  • PreCommit:协调者询问参与者是否可以预提交事务,如果参与者发生了错误返回No,或者因为网络原因没有返回,协调者就发送abort请求,参与者接收到abort命令后中断事务。
  • DoCommit:协调者命令参与者正式提交事务,如果参与者没有接收到DoCommit命令,就回滚。

TCC

要将原来只需一个接口就可以实现的逻辑分为Try,Confirm,Cancel三个接口,所以比较复杂,一般不用。
相比于XA,解决了几个缺点:

  • 解决了协调者单点,由业务方发起并完成业务。业务管理器也变成多点,引入集群。

  • 同步阻塞:引入了超时补偿,不会锁定所有业务资源,并且资源转换为业务逻辑形式,粒度减小。

  • 数据一致性:有了补偿机制后,由业务活动管理器控制数据一致性。

  • Try阶段:尝试执行,完成所有业务检查(一致性),预留必须的资源(准隔离性)

  • Confirm阶段:确认要真正执行的业务,不作任何业务检查,只使用Try阶段预留的业务资源,Confirm操作满足幂等性。要求满足幂等设计。Confirm操作失败的话要重试。

  • Cancel阶段:取消执行,释放Try阶段预留的资源。Cancel满足幂等性。异常处理方案和Confirm基本一致。

Try阶段是对业务系统进行检查和资源预览。比如订单和存储操作,需要先检查库存数量是否够用,并且对库存进行预留。预留操作就是新建一个可用库存字段,并对这个可用库存字段进行操作。

本地消息表

在这里插入图片描述
这里假设一个分布式系统,该业务下有生产者A,消费者B。

  1. B调用A,A首先会更新对应的业务表DBA,还会给对应的消息表中插入一条数据,两个操作发生在同一个事务中。
  2. 系统A定期向MQ写入一条信息,如果失败会重试。
  3. 系统B消费MQ中的消息,并处理业务逻辑。如果没有成功接收到MQ的消息,会进行重试。如果B中业务失败了,会通知A进行回滚。

条件:

  1. 生产者,消费者的接口都要幂等(幂等在下面有解释)
  2. 生产者需要额外创建消息表
  3. 需要可以补偿(Cancel),如果消费者业务失败,要能让生产者回滚

容错机制

  1. 步骤1失败时,直接回滚。
  2. 步骤2,3中MQ的发送或接收失败时,可以重试
  3. 步骤3中消费者业务失败时,告知生产者回滚

幂等

什么是幂等?就是说执行多少次结果都是一样的。f(f(x))=f(x)),比如说点赞,如果点赞接口是点赞数量+1,那就是不幂等;而如果点赞接口是在数据库中确保有一条数据:点赞文章,点赞用户,点赞数通过该文章的点赞记录总和,就可以保证发送多次点赞请求都是一样的效果。这就是幂等的。

可靠消息最终一致性

在这里插入图片描述
和以上本地消息表的区别主要就是将本地消息表kafka换成了MQ,kafka是一个分布式日志系统,巴拉巴拉目前来说在作为消息队列中,还是MQ比较好,Rabbit和Rocket

  1. A发送prepare消息给MQ,如果发送失败就直接取消操作,如果发送成功就开始执行A业务。
  2. A完成自己的本地事务后,发送Confirm消息给MQ,如果发送失败的话就发送回滚消息。
  3. B定期从MQ取Confirm消息,并执行本地事务,并返回ACK。如果取Confirm消息失败,就不断重试。如果本地事务执行失败,就发送回滚请求。
  4. MQ会定期轮询所有Prepare消息,调用A提供的接口查询消息的处理情况,如果该Prepare消息的本地事务处理成功,就重新发送Confirm消息,否则就回滚该消息。

尽最大努力通知

是一种简单的柔性事务,一般是对时间要求不高的,也就是一致性要求不高,B的处理结果对于A不影响的那种。

  1. A执行完本地事务后,发送消息到MQ
  2. 有个服务专门消费MQ的消息,这个服务消费MQ的消息并且调用B的接口。
  3. B如果执行成功就OK,否则就定时重试,重试多次后实在不行就算了。

这里应该是如果B一直失败就放弃B的业务,但是A的业务已经提交Commit。所以B的业务得是无关紧要的业务。

实战

两阶段提交/XA

参考支付宝文档

TCC

一般不推荐。。。

本地消息表

跨行转账
A发起转账,扣取金额,并且向消息表中插入消息,如果A失败则失败,如果成功,就会向MQ中定期写入消息,MQ中的消息会被B实时消费,并且给B增加金额。如果失败则一直重试。
在这里插入图片描述
还有小米海外商城,用户订单发生变化,会记录到消息表中,然后定期向消息表中的记录写入到mq中,然后消费mq给用户发送邮件,短信等。

可靠消息最终一致性

目前只有rocketmq支持这种方案,应用场景也很多。注册成功后发送邮件,电商系统给用户发送优惠券等需要最终一致性的情况。

尽最大努力通知

常见的是支付回调。本系统的支付服务受到第三方服务通知支付成功,那么支付服务先更新自己库中的订单支付状态,然后同步通知订单服务,如果同步的通知失败了,就异步不断通知。
在这里插入图片描述
小米的话,还有订单数据同步,比如A发生订单数据变更,现将变更信息写入小米自己的MQ,然后该MQ异步处理该消息来调用B的接口,并且重试到最大次数。

参考

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值