zoukankan      html  css  js  c++  java
  • 分布式事务

    1. 什么是分布式事务?

    分布式事务用于在分布式系统中保证不同节点之间的数据一致性。

    举例:有两个系统--订单系统和库存系统,我们的业务逻辑在执行订单和库存的时候,需要保证要么都成功,要么都失败。在单机系统中,我们可以直接通过事务来保证,而在分布式系统中,我们就需要分布式事务保证了。

    2. 有哪些分布式的事务方案呢?

    2.1  两阶段提交方案(XA方案)

    2.2  TCC(补偿事务)

    2.3 本地消息表(异步确保)

    2.4 可靠消息最终一致性方案(MQ)

    2.5 最大努力方案

    2.6 三阶段提交方案

    2.7 Sagas 事务模型

    3. 两阶段提交方案(XA方案)

    XA 是一个两阶段提交协议,该协议分为以下两个阶段:

    • 第一阶段:事务协调器要求每个涉及到事务的数据库预提交(precommit)此操作,并反映是否可以提交.---一个投票的过程。
    • 第二阶段:事务协调器要求每个数据库提交数据;或者回滚数据;

    两阶段提交这种解决方案属于牺牲了一部分可用性来换取的一致性。

    优点: 原理简单,实验方便;尽量保证了数据的强一致,适合对数据强一致要求很高的关键领域。(其实也不能100%保证强一致)

    缺点: 实现复杂,牺牲了可用性,对性能影响较大,因为严重依赖于数据库层面来搞定复杂的事务,效率很低,不适合高并发高性能场景

    同步阻塞:在二阶段提交的执行过程中,所有的参与该事务操作的逻辑都会处于阻塞状态。

    单点问题:协调者出现问题,事务无法完成 还会处于锁定的状态。

     4. TCC(补偿事务)

    TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

    • Try 阶段主要是对业务系统做检测及资源预留
    • Confirm 阶段主要是对业务系统做确认提交,Try阶段执行成功并开始执行 Confirm阶段时,默认 Confirm阶段是不会出错的。即:只要Try成功,Confirm一定成功。
    • Cancel 阶段主要是在业务执行错误,需要回滚的状态下执行的业务取消,预留资源释放。

    因为这个事务回滚实际上是严重依赖于你自己写代码来回滚和补偿了,会造成补偿代码巨大,非常之恶心。一般用在跟钱相关的,跟钱打交道的,支付、交易相关的场景,严格保证分布式事务要么全部成功,要么全部自动回滚,严格保证资金的正确性。

     5. 本地消息表

    1)A系统在自己本地一个事务里操作同时插入一条数据到消息表;

    2)接着A系统将这个消息发送到MQ中去;

    3)B系统接收到消息之后,在一个事务里,往自己本地消息表里插入一条数据,同时执行其他的业务操作,如果这个消息已经被处理过了,那么此时这个事务会回滚,这样保证不会重复处理消息;

    4)B系统执行成功之后,就会更新自己本地消息表的状态以及A系统消息表的状态;

    5)如果B系统处理失败了,那么就不会更新消息表状态,那么此时A系统会定时扫描自己的消息表,如果有没处理的消息,会再次发送到MQ中去,让B再次处理;

    6)这个方案保证了最终一致性,哪怕B事务失败了,但是A会不断重发消息,直到B那边成功为止;

    这种方案遵循BASE理论,采用的是最终一致性,比较适合实际业务场景的,即不会出现像2PC那样复杂的实现(当调用链很长的时候,2PC的可用性是非常低的),也不会像TCC那样可能出现确认或者回滚不了的情况。

    优点: 一种非常经典的实现,避免了分布式事务,实现了最终一致性。

    缺点: 消息表会耦合到业务系统中,如果没有封装好的解决方案,会有很多杂活需要处理。严重依赖于数据库的消息表来管理事务。不适合高并发的场景。

    6.可靠消息最终一致性方案(MQ)

    1)A系统先发送一个prepared消息到mq,如果这个prepared消息发送失败那么就直接取消操作别执行了

    2)如果这个消息发送成功过了,那么接着执行本地事务,如果成功就告诉mq发送确认消息,如果失败就告诉mq回滚消息

    3)如果发送了确认消息,那么此时B系统会接收到确认消息,然后执行本地的事务

    4)mq会自动定时轮询所有prepared消息回调你的接口,问你,这个消息是不是本地事务处理失败了,所有没发送确认消息?那是继续重试还是回滚?一般来说这里你就可以查下数据库看之前本地事务是否执行,如果回滚了,那么这里也回滚吧。这个就是避免可能本地事务执行成功了,别确认消息发送失败了。

    5)这个方案里,要是系统B的事务失败了咋办?重试咯,自动不断重试直到成功,如果实在是不行,要么就是针对重要的资金类业务进行回滚,比如B系统本地回滚后,想办法通知系统A也回滚;或者是发送报警由人工来手工回滚和补偿

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

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

    7 最大努力方案

    1)系统A本地事务执行完之后,发送个消息到MQ

    2)这里会有个专门消费MQ的最大努力通知服务,这个服务会消费MQ然后写入数据库中记录下来,或者是放入个内存队列也可以,接着调用系统B的接口

    3)要是系统B执行成功就ok了;要是系统B执行失败了,那么最大努力通知服务就定时尝试重新调用系统B,反复N次,最后还是不行就放弃

    8. 三阶段提交方案

    阶段一:事务询问(canCommit请求),询问参与者;参与者想协调者反馈事务询问的响应。

    阶段二:协调者根据参与者的反馈决定是否可以进行事务的PreCommit操作,1)执行事务预提交2)中断事务

    阶段三:执行提交或者中断事务;

    在阶段三中,存在的故障:协调者出现问题,协调者和参与者之间的网络出现故障;在这种异常中,参与者在等待超时后,会继续进行事务的提交。

    优点:相对于二阶段提交,此方法有点在于降低了参与者阻塞的范围,并且能够在出现单点故障后继续达成一致。

    缺点:在参与者接受到preCommit消息后,出现网络分区(部分通信不可用),此时协调者所在的节点和参与者无法进行通信,此时参与者会依然提交事务,会导致数据的不一致。

    9. Sagas 事务模型

    长时间运行的事务(Long-running-transaction)---比较新

    该模型核心思想就是拆分分布式系统中的长事务为多个短事务,或者叫多个本地事务,然后由 Sagas 工作流引擎负责协调,如果整个流程正常结束,那么就算是业务成功完成,如果在这过程中实现失败,那么Sagas工作流引擎就会以相反的顺序调用补偿操作,重新进行业务回滚。

    10. 总结

    分布式事务用在需要严格保证一致性的地方,比如资金方便;分布式事务不是越多越好的,代码复杂,性能变差,系统吞吐量下降,性能下跌。大多数情况是不需要用分布式事务的,可以直接通过监控(邮件,短信),记日志,事后定位、排查和解决、修复数据。此时的成本也比全部使用分布式事务的成本要低。

  • 相关阅读:
    Matrix-tree 定理的一些整理
    多项式
    多项式
    vijos 1641 Vs Snowy
    noip 提高组 2010
    军训有感
    我的将军啊
    洛谷 P3302 [SDOI2013]森林
    关于线段树
    关于KMP
  • 原文地址:https://www.cnblogs.com/ntbww93/p/9771456.html
Copyright © 2011-2022 走看看