分布式事物的解决方案

一:理论依据(讨论的前提)

  1. 本地事务、分布式事务(如果说本地事务是解决单个数据源上的数据操作的一致性问题的话,那么分布式事务则是为了解决跨越多个数据源上数据操作的一致性问题。
  2. 强一致性、弱一致性、最终一致性(对于关系型数据库,要求更新过的数据能被后续的访问都能看到,这是强一致性。如果能容忍后续的部分或者全部访问不到,则是弱一致性。如果经过一段时间后要求能访问到更新后的数据,则是最终一致性)

  3. CAP理论核心思想:分布式环境下(数据分布)要任何时刻保证数据一致性是不可能的,只能采取妥协的方案来保证数据最终一致性。这个也就是著名的CAP定理【C:一致性 A:可用性 P:分区容错性】)

  4. BASE 理论(BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的缩写。BASE理论是对CAP中一致性和可用性权衡的结果,其来源于对大规模互联网系统分布式实践的总结, 是基于CAP定理逐步演化而来的。BASE理论的核心思想是:即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到最终一致性。

二:分布式事物业界都有哪些解决方案

两阶段提交(2PC)

2pc是非常经典的强一致性、中心化的原子提交协议。中心化是指协议中有两类节点:一个中心化协调者节点(coordinator)和N个参与者节点(participant、cohort)。

  1. 两阶段提交,通过引入协调者来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。
  2. 准备阶段:协调者询问参与者事务是否执行成功,参与者发回事务执行结果。
  3. 提交阶段:如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。
  4. 需要注意的是:在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。
  • 存在的问题
  1. 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
  2. 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
  3. 数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
  4. 太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。
  5. 缺点:2PC/3PC需要资源管理器(mysql, redis)支持XA协议,且整个事务的执行期间需要锁住事务资源,会降低性能

三阶段提交(3PC/TCC)

  • 三阶段提交协议(3pc Three-phase_commit_protocol)主要是为了解决两阶段提交协议的阻塞问题,从原来的两个阶段扩展为三个阶段,并且增加了超时机制。
  •           
  1. 3PC只是解决了在异常情况下2PC的阻塞问题,但导致一次提交要传递6条消息,延时很大
  2. TCC是Try、Confirm、Cancel的缩写,在国内由于支付宝的布道而广为人知,TCC在保证强一致性的同时,最大限度提高系统的可伸缩性与可用性
  3. 我们假设一个完整的为业务包含一组子业务,Try操作完成所有的子业务检查,预留必要的业务资源,实现与其他事务的隔离;Confirm使用Try阶段预留的业务资源真正执行业务,而且Confirm操作满足幂等性,以遍支持重试;Cancel操作释放Try阶段预留的业务资源,同样也满足幂等性。“一次完整的交易由一系列微交易的Try 操作组成,如果所有的Try 操作都成功,最终由微交易框架来统一Confirm,否则统一Cancel,从而实现了类似经典两阶段提交协议(2PC)的强一致性。”
  4. 当然,TCC需要较的高开发成本,每个子业务都需要有响应的comfirm、Cancel操作,即实现相应的补偿逻辑。
  5. 总结:你原本的一个接口,要改造为3个逻辑,Try-Confirm-Cancel。需要增加额外的工作量,开源的tcc事物框架如:ByteTCC,tcc-transaction,himly。
  6. 3PC比2PC多了一个Confirm(确认)的阶段,2PC在得到参与者回复之后就执行了对事物的提交,而3PC是在try通过后,再进行Confirm逻辑(大概就是预提交阶段,比如库存预扣减等),最后Confirm成功之后才执行真正commit或者Cancel阶段

基于消息的分布式事务

    核心思想:将分布式事务分成多个本地事务

  1. 本地消息表这种方式其实就是在对业务表进行事物操作时,把对被操作的数据日志也一同写进本地库,这样就能利用本地事务来保证在对这两个表的操作满足事务特性,并且最后使用消息队列来保证最终一致性。
  2. 在分布式事务操作的一方完成写业务数据的操作之后向本地消息表发送一个消息,本地事务能保证这个消息一定会被写入本地消息表中。

  3. 之后将本地消息表中的消息转发到 MQ 等消息队列中,如果转发成功则将消息从本地消息表中删除,否则继续重新转发。(可以借助rocketMq的发送/消费失败重试机制,关于重复消费的问题需要自己做幂等来去处理)

  4. 在分布式事务操作的另一方从消息队列中读取一个消息,并执行消息中的操作

  5. 优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。(可控性好,编码实现相对简单,开发人员可根据自身业务特点来制定不同的策略来实现数据的最终一致性等)只能保证最终一致性;但可用性非常高,不会因为故障而发生阻

  6. 缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。

MQ 事务消息

       事务消息依赖于支持“事务消息”的消息队列,比如RocketMQ

  1. 有一些第三方的MQ是支持事务消息的,比如RocketMQ,
    他们支持事务消息的方式也是类似于采用的二阶段提交,
    但是市面上一些主流的MQ都是不支持事务消息的,比如 RabbitMQ 和 Kafka 都不支持。
  2. 以阿里的 RocketMQ 中间件为例,其思路大致为:
    第一阶段Prepared消息,会拿到消息的地址。第二阶段执行本地事务,第三阶段通过第一阶段拿到的地址去访问消息,并修改状态。
    也就是说在业务方法内要想消息队列提交两次请求,一次发送消息和一次确认消息。
    如果确认消息发送失败了RocketMQ会定期扫描消息集群中的事务消息,这时候发现了Prepared消息,
    它会向消息发送者确认,所以生产方需要实现一个check接口,
    RocketMQ会根据发送端设置的策略来决定是回滚还是继续发送确认消息。
    这样就保证了消息发送与本地事务同时成功或同时失败。

  3. 优点: 实现了最终一致性,不需要依赖本地数据库事务。、

  4. 缺点: 实现难度大,主流MQ不支持,RocketMQ事务消息部分代码也未开源。

方案对比

      

总结:

  1. 在分布式系统中,因为数据的分拆,服务的分拆,强一致性就很难保证。这个时候,用的最多的就是“最终一致性“。所以一般的在分布式系统中,通常都会把强一致性折中成最终一致性(减少系统的复杂度),从而变相的解决分布式事务问题。
  2. 很多场景,比如电商、网络购票,首先要保证的是高可用,不大可能采用强一致性,因此我们也会看到‘正在处理中…‘这种中间状态,后台很可能是异步处理的,在12306买过票的话都知道,下单成功到最后是否能出票由很长一段时间。并且在12306中抢票时明明看到还有很多票但是,就是出票失败,这就是他们采用了数据的最终一致性而不是强一致性来解决问题

                       

参考资源

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值