• Distributed


    总结

    • 可以看出 2PC 和 3PC 是一种强一致性事务,不过还是有数据不一致,阻塞等风险,而且只能用在数据库层面。
    • 而 TCC 是一种补偿性事务思想,适用的范围更广,在业务层面实现,因此对业务的侵入性较大,每一个操作都需要实现对应的三个方法。
    • 本地消息、事务消息和最大努力通知其实都是最终一致性事务,因此适用于一些对时间不敏感的业务。

    二阶段提交(2PC:Two-phase commit protocol)

    (1)2PC是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的。2PC引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是“准备阶段”和“提交阶段”。

    (2)2PC的具体流程:

    • “准备阶段”协调者会给各参与者发送准备命令,你可以把准备命令理解成除了提交事务之外啥事都做完了。
    • 同步等待所有资源的响应之后就进入第二阶段即“提交阶段”(注意提交阶段不一定是提交事务,也可能是回滚事务)。
    • 假如在第一阶段所有参与者都返回准备成功,那么协调者则向所有参与者发送提交事务命令,然后等待所有事务都提交成功之后,返回事务执行成功。

    • 假如在第一阶段有一个参与者返回失败,那么协调者就会向所有参与者发送回滚事务的请求,即分布式事务执行失败。

    (3)2PC的劣势:

    • 2PC是一种尽量保证强一致性的分布式事务,是同步阻塞的。而同步阻塞就导致长久的资源锁定问题,总体而言效率低
    • 并且存在单点故障问题,在极端条件下存在数据不一致的风险
    • 当协调者出现故障,再选举出新协调者后,难以判断当前协调者和数据库应该继续做什么操作-->详见协调者故障分析

    (4)2PC 适用于“数据库层面”的分布式事务场景,非数据库场景的分布式场景(如上传图片、发送短信)等不支持。

    (5)2PC,只是协议/理论指导,具体落地会有差异。

    三阶段提交(3PC: Three-phase commit protocol)

    (1)3PC 的出现是为了解决 2PC 的一些问题,相比于 2PC 它在参与者中也引入了超时机制,并且新增了一个阶段使得参与者可以利用这一个阶段统一各自的状态。也是一种是一种尽量保证强一致性的分布式事务。

    (2)3PC 包含了三个阶段,分别是准备阶段预提交阶段提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。

    (3) 3PC的具体流程:

    • “准备阶段”的变更成不会直接执行事务,而是会先去询问此时的参与者是否有条件接这个事务,因此不会一来就干活直接锁资源,使得在某些资源不可用的情况下所有参与者都阻塞着
    • “预提交阶段”的引入起到了一个统一状态的作用,它像一道栅栏,表明在预提交阶段前所有参与者其实还未都回应,在预处理阶段表明所有参与者都已经回应了。
    • “提交阶段”协调者则向所有参与者发送提交事务命令

     

    (4)3PC的优势

    • 3PC相较于2PC,在参与者中也引入了超时机制:参与者就不会傻等了,如果是等待提交命令超时,那么参与者就会提交事务了,因为都到了这一阶段了大概率是提交的,如果是等待预提交命令超时,那该干啥就干啥了,反正本来啥也没干。
    • 3PC 通过预提交阶段,可以减少故障恢复时候的复杂性,但是不能保证数据一致。所以说 3PC 就是通过引入预提交阶段来使得参与者之间的状态得到统一,也就是留了一个阶段让大家同步一下。新协调者来的时候发现有一个参与者处于预提交或者提交阶段,那么表明已经经过了所有参与者的确认了,所以此时执行的就是提交命令。

    (5)3PC的劣势

    • 引入一个阶段也多一个交互,因此性能会差一些,而且绝大部分的情况下资源应该都是可用的,这样等于每次明知可用执行还得询问一次
    • 然而超时机制也会带来数据不一致的问题,比如在等待提交命令时候超时了,参与者默认执行的是提交事务操作,但是有可能执行的是回滚操作,这样一来数据就不一致了。

    TCC(Try - Confirm - Cancel)

    (1)TCC 是一种补偿性事务思想,在业务层面代码里实现,适用的范围更广(2PC 和 3PC 都是数据库层面的,而 TCC 不仅包括数据库,还包括发送短信等)

    (2)TCC 的具体流程

    • Try 指的是预留,即资源的预留和锁定,注意是预留。
    • Confirm 指的是确认操作,这一步其实就是真正的执行了。
    • Cancel 指的是撤销操作,可以理解为把预留阶段的动作撤销了。

    其实从思想上看和 2PC 差不多,都是先试探性的执行,如果都可以那就真正的执行,如果不行就回滚。TCC模型还有个事务管理者的角色,用来记录TCC全局事务状态并提交或者回滚事务。

    还有一点要注意,撤销和确认操作的执行可能需要重试,因此还需要保证操作的幂等 -- 幂等问题

     

    (3)TCC 优点

    • TCC是在业务上实现的,所以可以跨数据库、跨不同的业务系统来实现事务。

    (4)TCC 劣势

    • TCC对业务的侵入性较大,和业务紧耦合。
    • 开发量也更大,每一个操作都需要实现对应的三个方法,而且有时候你会发现这三个方法还真不好写。

    本地消息表

    (1)本地消息表其实就是利用了 各系统本地的事务 来实现分布式事务。顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中。

    (2)本地消息表其实实现的是最终一致性,容忍了数据暂时不一致的情况

    (3)本地消息表的具体流程:

    • 在执行业务的时候“将业务的执行”和“将消息放入消息表中的操作”放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。
    • 然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。
    • 如果调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。
    • 这时候有可能消息对应的操作不成功,因此也需要重试,重试就得保证对应服务的方法是幂等的,而且一般重试会有最大次数,超过最大次数可以记录下报警让人工处理。

    消息事务

    (1)消息事务实现的也是最终一致性,这里的分布式事务指的是,“发送方”和“订阅方”的各自本地事务。

    (2)消息事务的具体流程:

    • 第一步,先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,
    • 第二/三步,发送成功后发送方再执行本地事务。
    • 第四步,再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。
    • 第五步,如果是 Commit, 那么订阅方就能收到这条消息
    • 第六步,订阅方再做对应的本地事务操作
    • 第七步,做完了之后再消费broker的后续消息。如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。

    (3)可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。

    最大努力通知

    • 最大努力通知其实只是表明了一种柔性事务的思想:我已经尽力我最大的努力想达成事务的最终一致了。因此,本地消息表也可以算最大努力,事务消息也可以算最大努力。
    • 就本地消息表来说会有后台任务定时去查看未完成的消息,然后去调用对应的服务,当一个消息多次调用都失败的时候可以记录下然后引入人工,或者直接舍弃。这其实算是最大努力了。
    • 事务消息也是一样,当半消息被commit了之后确实就是普通消息了,如果订阅者一直不消费或者消费不了则会一直重试,到最后进入死信队列。其实这也算最大努力。
    • 适用于对时间不敏感的业务,例如短信通知。

    原文链接

    敖丙 -- 面试必问:分布式事务六种解决方案

  • 相关阅读:
    东芝开发板驱动OLED模块显示LOGO图片
    东芝MCU实现位带操作
    使用系统定时器SysTick实现精确延时微秒和毫秒函数
    VC++调试错误的解决方案
    #pragma once与 #ifndef的区别
    strcmp()字符串比较函数用法
    C、C++中的static和extern关键字
    error LNK1169 找到一个或多个多重定义的符号的解决方法
    vs2013编译obs源码
    Qt线程—QThread的使用--run和movetoThread的用法
  • 原文地址:https://www.cnblogs.com/frankcui/p/15335027.html
Copyright © 2020-2023  润新知