[TOC]
1. 从分布式架构开始说起
随着不断的迭代,越来越多的系统都转向分布式部署,分布式的各个节点之间通过消息进行通信和协调,此时,系统有以下特性
- 分布性:分布式系统的各个机器在空间中任意分布
- 对等性:分布式的各个节点是对等的,没有主、从之分。(主从是集群的一种数据/服务冗余手段)
- 并发性:同一个分布式系统的各个节点,可能并发地去操作一些共享资源,比如数据库可能是很多节点在操作
- 时钟不易统一: 分布式系统缺乏一个全局的统一时钟,各个节点之间通过消息进行通信时,不同节点上的时间可能不同,就导致不能把时间作为一个准确的衡量标准
- 故障总是发生:组成分布式系统的各个节点都难免发生故障,在设计时考虑到的故障,一般都会发生,墨菲定律~~
分布式环境也会发生一些问题如下
- 通信异常:集中式的部署到分布式部署,各个节点从 JVM 内部通信到了节点之间消息通信,这个情况下,消息的丢失和延迟变得非常普遍。
- 网络分区:当网络发生异常,分布式系统中众多的节点中只有很少一部分能够正常通信,其它的不能通信,这样就会出现原本需要很多节点一起参与的工作现在只有少部分节点完成了,此时就是很可能会发生数据不一致的情况,这种情况叫网络分区。
- 单点故障:每个节点都是会发生故障的,比如宕机或者被攻击了
通过上面的简单介绍,可想而知,分布式系统面临的挑战远远比单点系统面临的要多得多,为了尽可能的提供分布式一致性,业界有了大名鼎鼎的 分布式一致性框架 zookeeper,基于它,我们可以让我们的分布式系统提供更高的可用性。
2. 事物简介与分布式事物
事物是一个系列的数据操作步骤在逻辑上的执行单元,一般我们都在数据库方面说的很多。事物的作用就是保证数据的一致性和正确性。在多个应用线程并发访问数据库时,事物可以在这些应用程序之间提供一个隔离的方法防止互相干扰。事物有 4 个特性 ACID(原子性、隔离性、一致性、持久性)。这里只简单聊聊隔离性和一致性。一致性指的是事物操作前后,数据库的状态应该是一致的,比如转账,不会出现 A 账户钱减少了 100,B 账户没增加钱或者增加了不是 100。隔离性指的是在并发环境中,各个事物的执行应该是互不干扰的。同时在标准的 SQL 规范中定义了 4 个事物的隔离级别,不同的隔离级别下对事物的处理不同。
- 读未提交:这种级别下允许脏读,隔离级别最低,在事物 A 中修改了某行数据但是事物 A 并没有提交,事物 B 过来的时候也可以读取到这一行修改后的数据。比如事物 A 需要把 1 自增一直到 10之后再提交事物,那么事物 B 读取到的 2-9 都是未提交的。
- 读已提交:事物 A 还是将 1 自增一直到 10,事物 B 只能读取到 10,假设事物 C 将 10 自增到 20,事物 B 也只能看到 20.
- 可重复读:在事物的A 自增 1 到 10 的过程中,事物 B 读取到的这行数据在事物 A 执行的过程中(此时事物 A 没有提交),读取到的值应该一直是 1
- 串行化:是最严格的数据级别,所有的事物串行执行,没有并发的可能。
2.1 分布式事物系统
基于单台机器的事物 ACID 在现在的应用系统中几乎是都满足了的,但是在分布式领域中,数据可能水平拆分在不同的机器上,这就会带来许许多多的问题,比如网络的故障、机器的宕机等,所以业内就专门对此研究才有了分布式事物的这么一说,当下也有已经应用于生产的比较流行的分布式事物框架比如 TCC 等,当然,最近阿里刚开源的 Fescar 也是很棒的可以去了解下。那么分布式事物到底是什么,其实就是指参与者、支持事物的服务器、资源服务器以及对应的事物管理器在不同的节点上。
举例子就是最经典的银行转账问题,从农行账户转账 100 块到工行账户,农行和工行肯定是不同的系统不同的服务器的,所以需要保证转账过程中一致性的问题,也就是转账后农行账户钱少了 100 块而工行的钱多了 100 块,如果这个过程出现问题了,比如农行的钱没减少,那么工行的钱也不能增加。这就需要一系列的协调者参与进来才能保证这些操作的一致性。
2.2 CAP & BASE
对于本地的事物处理来说,本地事物能够很容易满足 ACID 的特性,但是分布式事物的出现,就需要实现一套严格的满足 ACID 的分布式事物。在分布式的领域中,系统的可用性与严格一致性总是出现一些冲突,它们就像是轻微互斥一样的出现在系统中,满足了严格的一致性的话很容易会失去一些系统的可用性,而可用性又是系统运行的基本保障,于是如何协调好它们之间的关系就出现了一些基本理论,也就是 CAP 和 BASE
2.2.1 CAP 定理
CAP
- 一致性:一致性指的是数据在多个系统之间都存在,在一个系统数据执行更新操作后,保证所有的系统都处于一致的状态
- 可用性:可用性指的是系统要一直可以提供服务,对于用户的请求总是能够在规定时间内返回结果。
- 分区容错性:集群中某个节点挂了,系统整体要依然能够提供网络服务,除非是整个系统都挂了。
CAP 定理是一致性、可用性、分区容错性不可能同时满足,最多满足其中 2 项,而对于一个分布式系统而言,分区容错是基本要求。所以我们经常在一致性可用性之间寻找平衡点。
- 放弃分区容错:就是把集群放在一个节点上,这样就不能水平扩展了,这就不是分布式了,这个不会放弃的
- 放弃可用性:一旦系统的网络故障,那么被影响的服务需要等待一定的时间,在此期间系统不可用,无法提供服务
- 放弃一致性:这个一般不能放弃的,如果系统不能满足一致性,那谁还敢用你的系统,事实上这里指的是强一致性,而保留数据的最终一致性,也就是某段时间不一致,但是最终总是会达到一致性。
2.2.2 BASE 理论
BASE 是基本可用、软状态、最终一致性的意思。是对 CAP 中一致性和可用性平衡的结果。核心思想是即使无法做到强一致性,但是每个应用都根据自己的业务特点采用适当的方式使得系统达到最终一致性。
- 基本可用:分布式系统出现不可预知的故障时,允许损失部分可用性,比如服务降级
- 弱状态:允许系统中的数据存在中间状态并且这个中间状态不能影响系统的整体可用性,也即是不同节点的数据进行数据同步的过程存在一些延迟
- 最终一致性:强调的是系统中的所有数据副本在一段时间后总是达到一致的状态,它不实时保证系统数据的强一致性。
总的来说 BASE 理论面向的是大型高可用可扩展的分布式系统,和传统事物的 ACID 特性相反,它提出通过牺牲强一致性来获得可用性,并且允许数据在一段时间内是不一致的,最终会达到一致。
3. 一致性协议 2PC、3PC
上面简单聊过,设计系统时往往更多的是在平衡系统的可用性和一致性,于是就产生了一致性的协议,比如经典的二阶段提交协议和三阶段提交协议以及 Paxos 算法了。在分布式中,每个节点都知道自己的事物执行成功或者失败,而很难直接得到其他节点事物的执行情况,为了满足这个需求,我们一般会引入一个协调事物的组件,也被我们经常称之为事物管理器,它被用来协调参与者的行为并且决定是否要真正提交事物。基于这个思想,出现了 2 个比较有名的协议 2PC 3PC。
3.1 2PC
2PC 就是二阶段事物提交,通常应用在数据库领域中,目前绝大多数的关系型数据库都是采用的二阶段事物提交来完成事物的处理的,利用 2PC 可以统一提交或者统一回滚从而保证分布式数据的一致性。它把事物的提交过程分为 2 个阶段,第一阶段是提交事物请求,第二阶段是执行事物提交,核心是对每个事物都采用先尝试执行再决定提交的处理方式,阶段二可以看做一个强一致性的过程,具体过程如下
- 提交事物请求,也称投票阶段
- 事物询问:协调者向所有的参与者发送是否可以执行事物的提交询问
- 执行事物:各个参与者执行事物操作,但是并不提交事物,只是记录相关信息(这些信息可以用来回滚)
- 参与者返回给协调者信息:参与者们,成功执行事物的返回给协调者 YES,表示事物可以执行。否则就返回 NO
协调者收到各个参与者的返回后,接下来就分为 2 种情况,提交事物或者回滚事物
- 投票阶段都返回 YES 执行事物提交,在这里真正提交,此时所有参与者返回 YES
- 发送提交请求:协调者告诉所有参与者可以提交发出 commit 请求
- 事物提交:参与者收到 commit 请求后执行事物提交并提交后释放事物占用的资源
- 反馈事物的提交结果:参与者提交完成后向协调者发送 ACK 确认消息以确认提交完成
- 协调者收到参与者发送的 ACK 消息,完成事物
- 回滚事物,当投票阶段有返回 NO 的或者超时了的,就开始执行中断回滚操作
- 协调者向所有参与者发送 Rollback 请求
- 参与者收到 Rollback 请求后利用投票阶段记录的信息(一般是 undo log),来执行事物的回滚操作,并在回滚完释放事物资源
- 回滚完成后,向协调者发送 ACK 消息确认回滚完成
- 协调者收到所有参与者发送的 ACK 消息后完成事物回滚。
2PC的流程简图如下
二阶段协议还是有一些优缺点的,优点很明显,从上面图就能看出来,原理很简单,所以比较好实现。缺点就是从上面图里可以看到的一些问题,比如同步阻塞、单店问题、脑裂(数据不一致)、缺乏容错机制等
- 同步阻塞:同步会很限制系统的处理能力,在二阶段提交的过程中 , 所有参与该事物操作的逻辑都堵塞着,直到所有的参与者都完成了
- 单点问题:协调者的角色很重要,协调者如果出现了问题,就会很麻烦。
- 数据不一致:阶段二的时候,协调者发送 commit 的请求,由于网络问题只有部分参与者收到了,其它没收到的无法 commit。这就是脑裂的情况,整个系统出现了不一致的情况。
- 二阶段提交协议没有考虑设计容错机制,任何一个节点失败都导致整个事物的失败。比如阶段一中参与者出现故障导致协调者始终无法获取所有参与者的信息只能依靠超时来判断回滚事物。
3.2 3PC
上面了解了 2PC 的一些过程并且也了解到了它的一些弊端,针对 2PC 的思想和弊端,诞生了 3PC 协议。这就是开源的世界的活力,某个技术如果有需要改进的,很快就会有很多的人去想办法,然后给出解决方案。3PC 是 2PC 的改进版本,它把 2PC 中的第二阶段也即是真正提交事物的阶段分为 2 个部分 -- 准备提交事物和真正提交事物。过程如下:
- 阶段一:CanCommit,协调者向所有参与者发送 canCommit 请求询问是否执行事物提交操作,各个参与者收到消息后根据自己情况回复 YES 或者 NO
- 阶段二 PreCommit
协调者根据自己情况执行事物预提交和中断事物- A. 所有参与者返回 YES,执行事物预提交
- 协调者向所有参与者发送 PreCommit 请求,进入 Prepared 阶段
- 参与者收到 preCommit 请求后执行事物并记录信息
- 参与者如果成功执行了事物操作,反馈 Ack 给协调者,同时等待最终指令:提交还是中止
- B. 有任一参与者返回 No,或者超时,就会中断事物
- 协调者向所有参与者发送 abort 中断请求
- 参与者无论是收到 abort 请求还是等待协调者请求过程中超时了,参与者都会中断事物,此时还没执行事物 无所谓回滚
阶段三:doCommit 该阶段会进行真正提交或者回滚中断事物
- A. 执行提交
- 协调者向参与者发送提交 doCommit 请求
- 参与者收到 doCommit 请求后执行提交
- 参与者完成事物之后向协调者发送 Ack 确认
- 协调者收到 Ack 确认,事物完成
- B. 中断事物
- 协调者向所有参与者发送中断 abort 请求
- 参与者收到之后利用记录信息进行回滚操作
- 参与者回滚完成发送 Ack 确认消息给协调者
- 协调者收到所有参与者的 Ack 消息,回滚完成中断事物
上面的过程需要注意的是,只要进入了阶段三,可能协调者会出现问题或者协调者和参与者网络不通,无论是哪种情况导致的参与者没收到 doCommit 消息或者 abort 消息参与者都会在超时后直接继续事物的提交。
3PC的流程简图如下:
三阶段协议也是由一些优缺点的,它是二阶段协议的升级版本,它最大的优点就是降低了参与者的阻塞范围,并且能够在出现单店故障后继续达成一致的状态。它的缺点是可能在参与者接收 preCommit 消息后,此时协调者所在的节点和参与者因为一些原因无法通信了但是参与者依然会对事物进行提交,这又会出现不一致的情况。
小结
本文主要了解了分布式的一些概念,以及 CAP定理和BASE理论,同时也了解了为保证一致性而产生的 2PC 和 3PC协议,它们各有优缺点各有各自的局限性,下篇文章将学习比较成熟的分布式一致性协议 paxos 和 zab 协议。
近况,毕业一年,第一次跳槽,工作交接之余总算是暂时空闲下来几天时间,趁着这个机会,学习这个一直没真正出发去完整学习的zookeeper,路线大致是:
解决什么问题 -> 怎么用 -> 关键流程的设计 -> 交流复盘
2019下半年重心基本就在熟悉新公司的业务和学习zookeeper上了