写点什么

我们究竟还要学习哪些 Java 知识?程序员翻身之路

发布于: 2021 年 08 月 02 日

背景知识

CAP 定理

CAP 定理,又被叫作布鲁尔定理。对于设计分布式系统来说(不仅仅是分布式事务)的架构师来说,CAP 就是你的入门理论。


? C (一致性):对某个指定的客户端来说,读操作能返回最新的写操作。对于数据分布在不同节点上的数据上来说,如果在某个节点更新了数据,那么在其他节点如果都能读取到这个最新的数据,那么就称为强一致,如果有某个节点没有读取到,那就是分布式不一致。


? A (可用性):非故障的节点在合理的时间内返回合理的响应(不是错误和超时的响应)。可用性的两个关键一个是合理的时间,一个是合理的响应。合理的时间指的是请求不能无限被阻塞,应该在合理的时间给出返回。合理的响应指的是系统应该明确返回结果并且结果是正确的,这里的正确指的是比如应该返回 50,而不是返回 40。


? P (分区容错性):当出现网络分区后,系统能够继续工作。打个比方,这里个集群有多台机器,有台机器网络出现了问题,但是这个集群仍然可以正常工作。

BASE 理论

BASE 理论指的是基本可用 Basically Available,软状态 Soft State,最终一致性 Eventual Consistency,核心思想是即便无法做到强一致性,但应该采用适合的方式保证最终一致性,是对 CAP 中 AP 的一个扩展。


BASE,Basically Available Soft State Eventual Consistency 的简写:


BA:Basically Available 基本可用,分布式系统在出现故障的时候,允许损失部分可用性,即保证核心可用。


S:Soft State 软状态,允许系统存在中间状态,而该中间状态不会影响系统整体可用性。


E:Consistency 最终一致性,系统中的所有数据副本经过一定时间后,最终能够达到一致的状态。


BASE 理论本质上是对 CAP 理论的延伸,是对 CAP 中 AP 方案的一个补充。

柔性事务

不同于 ACID 的刚性事务,在分布式场景下基于 BASE 理论,就出现了柔性事务的概念。要想通过柔性事务来达到最终的一致性,就需要依赖于一些特性,这些特性在具体的方案中不一定都要满足,因为不同的方案要求不一样;但是都不满足的话,是不可能做柔性事务的。

幂等操作

在编程中一个幂等操作的特点是其任意多次执行所产生的影响均与一次执行的影响相同。幂等函数,或幂等方法,是指可以使用相同参数重复执行,并能获得相同结果的函数。这些函数不会影响系统状态,也不用担心重复执行会对系统造成改变。例如,支付流程中第三方支付系统告知系统中某个订单支付成功,接收该支付回调接口在网络正常的情况下无论操作多少次都应该返回成功。

为什么需要分布式事务

随着业务的发展及服务的 SOA 化,一些大的操作往往由不同的小操作组成,而这些小的操作分布在不同的服务器上,分布式事务需要保证这些小操作要么全部成功,要么全部失败。从本质上来说,分布式事务是为了保证不同数据库的数据一致性。可能应用分布式事务的场景有:


  1. 数据库分库分表


当数据库单表数据达到千万级别,就要考虑分库分表,那么就会从原来的一个数据库变成多个数据库。例如如果一个操作即操作了 01 库,又操作了 02 库,而且又要保证数据的一致性,那么就要用到分布式事务。


  1. 应用 SOA 化


所谓的 SOA 化,就是业务的服务化。例如电商平台下单操作就会产生调用库存服务扣减库存和订单服务更新订单数据,那么就会设计到订单数据库和库存数据库,为了保证数据的一致性,就需要用到分布式事务。

两套协议、四类常见方案

两套协议是指两阶段提交协议 2PC 和三阶段提交协议 3PC


四类常见方案这里我们介绍以下分布式事务解决方案:Tcc、可靠消息最终一致性、最大努力通知、Saga

两套协议

两阶段提交协议 2PC

两阶段提交(2PC) 是 Oracle Tuxedo 系统提出的 XA 分布式事务协议的其中一种实现方式。


XA 协议中有两个重要角色:事务协调者事务参与者

漫话图解

2PC 协议有两个阶段:Propose 和 Commit. 在无 failure 情况下的 2PC 协议流程的画风是这样的:


? Propose 阶段:


? coordinator: "昨夜验人有惊喜, 今天都投票出六娃"


? voter1/voter2/voter3: "好的女王大人!"


? Commit 阶段


? coordinator: "投六娃"


? voter1/voter2/voter3: "投了女王大人!" (画外音: 六娃扑街)



图 1: 2PC, coordinator 提议通过, voter{1,2,3}达成新的共识


如果有至少一个 voter (比如 voter3)在 Propose 阶段投了反对票, 那么 propose 通过失败. coordinator 就会在 Commit(or abort)阶段跟所有 voter 说, 放弃这个 propose.



图 2: 2PC, coordinator 提议没有通过, voter{1,2,3}保持旧有的共识

具体流程

分布式事务中 2PC 的具体流程是这样的:


第一阶段


? 顺利的情况


  1. 事务协调者的节点会首先向所有的参与者节点发送 Prepare(预备) 请求。

  2. 在接到 Prepare(预备) 请求之后,每一个参与者节点会各自执行与事务有关的数据更新,写入 Undo Log 和 Redo Log。

  3. 参与者执行成功,暂时不提交事务,向事务协调节点返回 done(完成)消息。

  4. 进入第二阶段


? 出错时


在 XA 的第一阶段,如果某个事务参与者反馈失败消息,说明该节点的本地事务执行不成功,必须回滚。


  1. 事务协调者的节点会首先向所有的参与者节点发送 Prepare(预备) 请求。

  2. 在接到 Prepare(预备) 请求之后,每一个参与者节点会各自执行与事务有关的数据更新,写入 Undo Log 和 Redo Log。

  3. 参与者执行失败,返回失败消息。

  4. 协调者中断事务


中断事务


任何一个参与者向协调者反馈了 No 响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事物。


  1. 发送回滚请求。协调者向所有参与者节点发出 Rollback 请求。

  2. 事物回滚。参与者收到 Rollback 请求之后,会利用其在阶段一种记录的 Undo 信息来执行事务回滚操作,并在完成回滚之后释放在整个事物执行期间占用的资源。

  3. 反馈事物回滚结果。参与者在完成事物回滚之后,向协调者发送 Ack 消息。

  4. 中断事务


第二阶段


在 XA 分布式事务的第二阶段,如果事务协调节点在之前所收到都是正向返回,那么它将会向所有事务参与者发出 Commit 请求。


接到 Commit 请求之后,事务参与者节点会各自进行本地的事务提交,并释放锁资源。当本地事务完成提交后,将会向事务协调者返回“完成”消息。


当事务协调者接收到所有事务参与者的“完成”反馈,整个分布式事务完成。


优缺点


优点


2PC 是强一致(要打个问号)协议:


  1. 预备、提交两个阶段保证了事务是原子的

  2. 2PC 是允许读-写隔离的,这意味着某个字段的变更在事务协调者提交之前是不可见的。


缺点


  1. 同步阻塞:当参与事务者存在占用公共资源的情况,其中一个占用了资源,其他事务参与者就只能阻塞等待资源释放,处于阻塞状态。

  2. 单点故障:一旦事务管理器出现故障,整个系统不可用

  3. 数据不一致:在阶段二,如果事务管理器只发送了部分 commit 消息,此时网络发生异常,那么只有部分参与者接收到 commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。

  4. 不确定性:当协事务管理器发送 commit 之后,并且此时只有一个参与者收到了 commit,那么当该参与者与事务管理器同时宕机之后,重新选举的事务管理器无法确定该条消息是否提交成功。

三阶段提交协议 3PC

漫话图解

简单的说来, 3PC 就是把 2PC 的 Commit 阶段拆成了 PreCommit 和 Commit 两个阶段. 通过进入增加的这一个 PreCommit 阶段, voter 可以得到 Propose 阶段的投票结果, 但不会 commit; 而通过进入 Commit 阶段, voter 可以盘出其他每个 voter 也都打算 commit 了, 从而可以放心的 commit.


换言之, 3PC 在 2PC 的 Commit 阶段里增加了一个 barrier(即相当于告诉其他所有 voter, 我收到了 Propose 的结果啦). 在这个 barrier 之前 coordinator 掉线的话, 其他 voter 可以得出结论不是每个 voter 都收到 Propose Phase 的结果, 从而放弃或选出新的 coordinator; 在这个 barrier 之后 coordinator 掉线的话, 每个 voter 会放心的 commit, 因为他们知道其他 voter 也都做同样的计划.



图 3: 3PC, coordinator 提议通过, voter{1,2,3}达成新的共识

具体流程

阶段一 CanCommit


  1. 事务询问:Coordinator 向各参与者发送 CanCommit 的请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应;

  2. 参与者向 Coordinator 反馈询问的响应:参与者收到 CanCommit 请求后,正常情况下,如果自身认为可以顺利执行事务,那么会反馈 Yes 响应,并进入预备状态,否则反馈 No。


阶段二 PreCommit


执行事务预提交:如果 Coordinator 接收到各参与者反馈都是 Yes,那么执行事务预提交:


  1. 发送预提交请求:Coordinator 向各参与者发送 preCommit 请求,并进入 prepared 阶段;

  2. 事务预提交:参与者接收到 preCommit 请求后,会执行事务操作,并将 Undo 和 Redo 信息记录到事务日记中;

  3. 各参与者向 Coordinator 反馈事务执行的响应:如果各参与者都成功执行了事务操作,那么反馈给协调者 ACK 响应,同时等待最终指令,提交 commit 或者终止 abort,结束流程;


中断事务:如果任何一个参与者向 Coordinator 反馈了 No 响应,或者在等待超时后,Coordinator 无法接收到所有参与者的反馈,那么就会中断事务。


  1. 发送中断请求:Coordinator 向所有参与者发送 abort 请求;

  2. 中断事务:无论是收到来自 Coordinator 的 abort 请求,还是等待超时,参与者都中断事务。


阶段三 doCommit


执行提交


  1. 发送提交请求:假设 Coordinator 正常工作,接收到了所有参与者的 ack 响应,那么它将从预提交阶段进入提交状态,并向所有参与者发送 doCommit 请求;

  2. 事务提交:参与者收到 doCommit 请求后,正式提交事务,并在完成事务提交后释放占用的资源;

  3. 反馈事务提交结果:参与者完成事务提交后,向 Coordinator 发送 ACK 信息;

  4. 完成事务:Coordinator 接收到所有参与者 ack 信息,完成事务。


中断事务:假设 Coordinator 正常工作,并且有任一参与者反馈 No,或者在等待超时后无法接收所有参与者的反馈,都会中断事务


  1. 发送中断请求:Coordinator 向所有参与者节点发送 abort 请求;

  2. 事务回滚:参与者接收到 abort 请求后,利用 undo 日志执行事务回滚,并在完成事务回滚后释放占用的资源;

  3. 反馈事务回滚结果:参与者在完成事务回滚之后,向 Coordinator 发送 ack 信息;

  4. 中断事务:Coordinator 接收到所有参与者反馈的 ack 信息后,中断事务。


优缺点


? 优化单点故障:相比二阶段提交,三阶段提交降低了阻塞范围,在等待超时后协调者或参与者会中断事务。避免了协调者单点问题。阶段 3 中协调者出现问题时,参与者会继续提交事务。


? 一致性问题:

四种方案

Saga

1987 年普林斯顿大学的 Hector Garcia-Molina 和 Kenneth Salem 发表了一篇 Paper Sagas,讲述的是如何处理 long lived transaction(长活事务)。Saga 是一个长活事务可被分解成可以交错运行的子事务集合。其中每个子事务都是一个保持数据库一致性的真实事务。


分布式事务执行过程中,依次执行各参与者的正向操作,如果所有正向操作均执行成功,那么分布式事务提交。如果任何一个正向操作执行失败,那么分布式事务会去退回去执行前面各参与者的逆向回滚操作,回滚已提交的参与者,使分布式事务回到初始状态。



Saga 模式用一种非常纯朴的方式来处理一致性:补偿。上图左侧是正常的事务流程,当执行到 T3 时发生了错误,则开始执行右边的事务补偿流程,反向执行 T3、T2、T1 的补偿服务,其中 C3 是 T3 的补偿服务、C2 是 T2 的补偿服务、C1 是 T1 的补偿服务,将 T3、T2、T1 已经修改的数据补偿掉。

Tcc

TCC 是 Try、Confirm、Cancel 三个词语的缩写,TCC 要求每个分支事务实现三个操作:预处理 Try、确认 Confirm、撤销 Cancel。


? Try 操作做业务检查及资源预留,


? Confirm 做业务确认操作,


? Cancel 实现一个与 Try 相反的操作即回滚操作。


TM 首先发起所有的分支事务的 try 操作,任何一个分支事务的 try 操作执行失败,TM 将会发起所有分支事务的 Cancel 操作,若 try 操作全部成功,TM 将会发起所有分支事务的 Confirm 操作,其中 Confirm/Cancel 操作若执行失败,TM 会进行重试。



分支事务失败的情况:



TCC 分为三个阶段:


  1. Try 阶段是做业务检查(一致性)及资源预留(隔离),此阶段仅是一个初步操作,它和后续的 Confirm 一起才能 真正构成一个完整的业务逻辑。

  2. Confirm 阶段是做确认提交,Try 阶段所有分支事务执行成功后开始执行 Confirm。通常情况下,采用 TCC 则 认为 Confirm 阶段是不会出错的。即:只要 Try 成功,Confirm 一定成功。若 Confirm 阶段真的出错了,需引 入重试机制或人工处理。

  3. Cancel 阶段是在业务执行错误需要回滚的状态下执行分支事务的业务取消,预留资源释放。通常情况下,采 用 TCC 则认为 Cancel 阶段也是一定成功的。若 Cancel 阶段真的出错了,需引入重试机制或人工处理。


TM 事务管理器可以实现为独立的服务,也可以让全局事务发起方充当 TM 的角色,TM 独立出来是为了成为公用组件,是为了考虑系统结构和软件复用。


TM 在发起全局事务时生成全局事务记录,全局事务 ID 贯穿整个分布式事务调用链条,用来记录事务上下文, 追踪和记录状态,由于 Confirm 和 cancel 失败需进行重试,因此需要实现为幂等,幂等性是指同一个操作无论请求 多少次,其结果都相同。

可靠消息最终一致性

可靠消息最终一致性方案是指当事务发起方执行完成本地事务后并发出一条消息,事务参与方(消息消费者)一定能够接收消息并处理事务成功,此方案强调的是只要消息发给事务参与方最终事务要达到一致。


此方案是利用消息中间件完成,如下图:



事务发起方(消息生产方)将消息发给消息中间件,事务参与方从消息中间件接收消息,事务发起方和消息中间件之间,事务参与方(消息消费方)和消息中间件之间都是通过网络通信,由于网络通信的不确定性会导致分布式事务问题。


因此可靠消息最终一致性方案要解决以下几个问题:


  1. 本地事务与消息发送的原子性问题


本地事务与消息发送的原子性问题即:事务发起方在本地事务执行成功后消息必须发出去,否则就丢弃消息。即实现本地事务和消息发送的原子性,要么都成功,要么都失败。本地事务与消息发送的原子性问题是实现可靠消息最终一致性方案的关键问题。

最后

权威指南-第一本 Docker 书

引领完成 Docker 的安装、部署、管理和扩展,让其经历从测试到生产的整个开发生命周期,深入了解 Docker 适用于什么场景。并且这本 Docker 的学习权威指南介绍了其组件的基础知识,然后用 Docker 构建容器和服务来完成各种任务:利用 Docker 为新项目建立测试环境,演示如何使用持续集成的工作流集成 Docker,如何构建应用程序服务和平台,如何使用 Docker 的 API,如何扩展 Docker。


总共包含了:简介、安装 Docker、Docker 入门、使用 Docker 镜像和仓库、在测试中使用 Docker、使用 Docker 构建服务、使用 Fig 编配 Docke、使用 Docker API、获得帮助和对 Docker 进行改进等 9 个章节的知识。






关于阿里内部都在强烈推荐使用的“K8S+Docker 学习指南”—《深入浅出 Kubernetes:理论+实战》、《权威指南-第一本 Docker 书》,看完之后两个字形容,爱了爱了!


如果你也爱了,点击这里免费下载:“K8S+Docker”学习指南

用户头像

还未添加个人签名 2021.07.29 加入

还未添加个人简介

评论

发布
暂无评论
我们究竟还要学习哪些Java知识?程序员翻身之路