从Paxos到Zookeeper:分布式一致性原理与实践 读书笔记

237 阅读19分钟

分布式架构

集中式架构特点

  1. 单体架构,数据集中存储于这个中心节点中,并且整个系统的所有业务单元都集中部署在这个中心节点上,系统的所有功能均由其集中处理。
  2. 优点:开发与管理相对容易,不存在分布式一致性的问题
  3. 缺点:扩展性低,价格昂贵,单点问题

分布式架构特点

  1. 分布式系统是一个硬件或软件组件分布在不同的网络计算机上,彼此之间仅仅通过消息传递进行通信和协调的系统。
  2. 优点:扩展性强,成本低,可用性高,服务解耦
  3. 缺点:治理困难,数据一致性问题,通信异常、网络分区

从ACID到CAP/BASE

ACID

  1. 事务具有四个特征,分别是原子性(Atomic-ity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability),简称为事务的ACID特性。

  2. 原子性:事务不可分割线,要么全部成功,要么全部失败:undo log保证事务的原子性

  3. 隔离性

    1. 读未提交:一个事务可以读取到另一个事务未提交的修改。这会带来脏读、幻读、不可重复读问题。(基本没用)
    2. 读已提交:一个事务只能读取另一个事务已经提交的修改。其避免了脏读,但仍然存在不可重复读和幻读问题。
    3. 可重复读:同一个事务中多次读取相同的数据返回的结果是一样的。其避免了脏读和不可重复读问题,但幻读依然存在。
    4. 串行化:事务串行执行。避免了以上所有问题。
  4. 永久性:指一个事务一旦提交,它对数据库中对应数据的状态变更就应该是永久性的。red log支持永久性

  5. 一致性:原子+隔离+永久保证一致性

CAP理论

CAP理论告诉我们,一个分布式系统不可能同时满足一致性(C:Consistency)、可用性(A:Availability)和分区容错性(P:Partition toler-ance)这三个基本需求,最多只能同时满足其中的两项。

  1. 一致性: 一致性是指数据在多个副本之间是否能够保持一致的特性。在一致性的需求下,当一个系统在数据一致的状态下执行更新操作后,应该保证系统的数据仍然处于一致的状态。
  2. 可用性: 可用性是指系统提供的服务必须一直处于可用的状态,对于用户的每一个操作请求总是能够在有限的时间内返回结果。这里我们重点看下“有限的时间内”和“返回结果”。
  3. 分区容错性: :分布式系统在遇到任何网络分区故障的时候,仍然需要能够保证对外提供满足一致性和可用性的服务,除非是整个网络环境都发生了故障。

分布式CAP定理,为什么不能同时满足三个特性?_公众号:鄙人薛某-CSDN博客_cap理论 三个特性

BASE理论

  1. BASE是Basically Available(基本可用)、Soft state(软状态)和Eventually consistent(最终一致性)三个短语的简写

  2. 基本可用: 基本可用是指分布式系统在出现不可预知故障的时候,允许损失部分可用性;

    1. 响应时间上的损失:耗时
    2. 功能上的损失:降级
  3. 软状态: 指允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行数据同步的过程存在延时。

  4. 最终一致性: 最终一致性强调的是系统中所有的数据副本,在经过一段时间的同步后,最终能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。

一致性协议

2PC 二阶段提交

  1. 阶段一:提交事务请求

      1. 事务询问。协调者向所有的参与者发送事务内容,询问是否可以执行事务提交操作,并开始等待各参与者的响应。
      2. 执行事务。各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中。
      3. 各参与者节点执行事务操作,并将Undo和Redo信息记入事务日志中。如果参与者成功执行了事务操作,那么就反馈给协调者Yes响应,表示事务可以执行;如果参与者没有成功执行事务,那么就反馈给协调者No响应,表示事务不可以执行。
  2. 执行事务提交:假如协调者从所有的参与者获得的反馈都是Yes响应,那么就会执行事务提交。

      1. 发送提交请求。协调者向所有参与者节点发出Commit请求。
      2. 事务提交。参与者接收到Commit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。
      3. 反馈事务提交结果。参与者在完成事务提交之后,向协调者发送Ack消息。
      4. 完成事务。协调者接收到所有参与者反馈的Ack消息后,完成事务。
  3. 中断事务:假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务

      1. 发送回滚请求。发送回滚请求。
      2. 事务回滚。参与者接收到Rollback请求后,会利用其在阶段一中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。
      3. 反馈事务回滚结果。参与者在完成事务回滚之后,向协调者发送Ack消息。
      4. 中断事务。协调者接收到所有参与者反馈的Ack消息后,完成事务中断。
  4. 优缺点

      1. 优点:原理简单,实现方便。

      2. 缺点:

        1. 同步阻塞:在二阶段提交的执行过程中,所有参与该事务操作的逻辑都处于阻塞状态
        2. 单点问题:一旦协调者出现问题,那么整个二阶段提交流程将无法运转,更为严重的是,如果协调者是在阶段二中出现问题的话,那么其他参与者将会一直处于锁定事务资源的状态中,而无法继续完成事务操作。

3PC 三阶段提交

  1. 阶段一:CanCommit

    1. 事务询问。协调者向所有的参与者发送一个包含事务内容的canCommit请求,询问是否可以执行事务提交操作,并开始等待各参与者的响应。
    2. 各参与者向协调者反馈事务询问的响应。参与者在接收到来自协调者的canCommit请求后,正常情况下,如果其自身认为可以顺利执行事务,那么会反馈Yes响应,并进入预备状态,否则想要No。
  2. 阶段二:PreCommit:在阶段二中,协调者会根据各参与者的反馈情况来决定是否可以进行事务的PreCommit操作,正常情况下,包含两种可能。

    1. 执行事务预提交

      1. 发送预提交请求。协调者向所有参与者节点发出preCommit的请求,并进入Prepared阶段。
      2. 事务预提交。参与者接收到preCommit请求后,会执行事务操作,并将Undo和Redo信息记录到事务日志中。
      3. 各参与者向协调者反馈事务执行的响应。如果参与者成功执行了事务操作,那么就会反馈给协调者Ack响应,同时等待最终的指令:提交(commit)或中止(abort)。
    2. 中断事务:假如任何一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

      1. 发送中断请求。协调者向所有参与者节点发出abort请求。
      2. 中断事务。无论是收到来自协调者的abort请求,或者是在等待协调者请求过程中出现超时,参与者都会中断事务。
  3. 阶段三:doCommit

    1. 执行提交

      1. 发送提交请求。进入这一阶段,假设协调者处于正常工作状态,并且它接收到了来自所有参与者的Ack响应,那么它将从“预提交”状态转换到“提交”状态,并向所有的参与者发送doCommit请求。
      2. 事务提交。参与者接收到doCommit请求后,会正式执行事务提交操作,并在完成提交之后释放在整个事务执行期间占用的事务资源。
      3. 反馈事务提交结果。参与者在完成事务提交之后,向协调者发送Ack消息。
      4. 完成事务。协调者接收到所
      5. 有参与者反馈的Ack消息后,完成事务。
    2. 中断事务:进入这一阶段,假设协调者处于正常工作状态,并且有任意一个参与者向协调者反馈了No响应,或者在等待超时之后,协调者尚无法接收到所有参与者的反馈响应,那么就会中断事务。

      1. 发送中断请求。协调者向所有的参与者节点发送abort请求。
      2. 事务回滚。参与者接收到abort请求后,会利用其在阶段二中记录的Undo信息来执行事务回滚操作,并在完成回滚之后释放在整个事务执行期间占用的资源。
      3. 反馈事务回滚结果。参与者在完成事务回滚之后,向协调者发送Ack消息。
      4. 中断事务。协调者接收到所有参与者反馈的Ack消息后,中断事务。
    3. 备注

需要注意的是,一旦进入阶段三,可能会存在以下两种故障。

•协调者出现问题。

•协调者和参与者之间的网络出现故障。

无论出现哪种情况,最终都会导致参与者无法及时接收到来自协调者的doCommit或是abort请求,针对这样的异常情况,参与者都会在等待超时之后,继续进行事务提交。

  1. 优缺点

    1. 相较于二阶段提交协议,三阶段提交协议最大的优点就是降低了参与者的阻塞范围,并且能够在出现单点故障后继续达成一致。
    2. 三阶段提交协议在去除阻塞的同时也引入了新的问题,那就是在参与者接收到preCommit消息后,如果网络出现分区,此时协调者所在的节点和参与者无法进行正常的网络通信,在这种情况下,该参与者依然会进行事务的提交,这必然出现数据的不一致性。

Paxos算法

  1. 它利用大多数 (Majority) 机制保证了2F+1的容错能力,即2F+1个节点的系统最多允许F个节点同时出现故障。
  2. 一个或多个提议进程 (Proposer) 可以发起提案 (Proposal),Paxos算法使所有提案中的某一个提案,在所有进程中达成一致。系统中的多数派同时认可该提案,即达成了一致。最多只针对一个确定的提案达成一致。
  3. Paxos将系统中的角色分为提议者 (Proposer),决策者 (Acceptor),和最终决策学习者 (Learner)。

Zookeeper

  1. 概念:ZooKeeper是一个典型的分布式数据一致性的解决方案,分布式应用程序可以基于它实现诸如数据发布/订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能。

  2. zk可以保证CAP中的cp(一致性,分区容错性),因为在leader节点选举期间,会暂停对外提供服务。

  3. zk一致性特点:

    1. 顺序一致性:从同一个客户端发起的事务请求,最终将会严格地按照其发起顺序被应用到ZooKeeper中去。zk会为每个事务生成单调递增的事务编号zxid,事务按顺序执行。
    2. 最终一致性:ZooKeeper仅仅保证在一定的时间段内,客户端最终一定能够从服务端上读取到最新的数据状态。zk原子广播到leader节点,无法保证实时,只能保证最终一致性。
  4. 介绍

    1. 数据模型:zk的数据是存在内存中的,类似一个树形文件系统

      1. 节点类型

        1. 持久节点:是指该数据节点被创建后,就会一直存在于ZooKeeper服务器上,直到有删除操作来主动清除这个节点。
        2. 持久顺序节点:具有持久节点的特性,节点会加上序号
        3. 临时节点:临时节点的生命周期和客户端的会话绑定在一起,也就是说,如果客户端会话失效,那么这个节点就会被自动清理掉,无法创建子节点(分布式排他锁)。
        4. 临时顺序节点:具有临时节点特性,节点会加上序号,无法创建子节点(分布式共享锁)

Redis与Zookeeper实现分布式锁的区别 - __Meng - 博客园

  1. 服务器角色

    1. Leader:Leader服务器是整个ZooKeeper集群工作机制中的核心,其主要工作有以下两个。

      1. 事务请求的唯一调度和处理者,保证集群事务处理的顺序性。
      2. 集群内部各服务器的调度者。
    2. Follower:ZooKeeper集群状态的跟随者,其主要工作有以下三个。

      1. 处理客户端非事务请求,转发事务请求给Leader服务器。
      2. 参与事务请求Proposal的投票。
      3. 参与Leader选举投票。
    3. Observer:只负责读请求

  2. 会话

    1. CON-NECTING:客户端开始创建zk对象时状态
    2. CONNECTED:客户端成功连接上服务器,然后将客户端状态变更为CONNECTED
    3. CLOSE:出现诸如会话超时、权限检查失败或是客户端主动退出程序等情况,那么客户端的状态就会直接变更为CLOSE。
  3. ZAB协议:支持崩溃恢复的原子广播协议,zk依赖实现分布式一致性

    1. 概念: Leader服务器负责将一个客户端事务请求转换成一个事务Proposal(提议),并将该Proposal分发给集群中所有的Follower服务器。之后Leader服务器需要等待所有Fol-lower服务器的反馈,一旦超过半数的Follower服务器进行了正确的反馈后,那么Leader就会再次向所有的Follower服务器分发Commit消息,要求其将前一个Proposal进行提交。

    2. 消息广播

      1. 在整个消息广播过程中,Leader服务器会为每个事务请求生成对应的Proposal来进行广播,并且在广播事务Proposal之前,Leader服务器会首先为这个事务Proposal分配一个全局单调递增的唯一ID,我们称之为事务ID(即ZXID)。由于ZAB协议需要保证每一个消息严格的因果关系,因此必须将每一个事务Proposal按照其ZXID的先后顺序来进行排序与处理。
      2. 具体的,在消息广播过程中,Leader服务器会为每一个Fol-lower服务器都各自分配一个单独的队列,然后将需要广播的事务Proposal依次放入这些队列中去,并且根据FIFO策略进行消息发送。每一个Follower服务器在接收到这个事务Proposal之后,都会首先将其以事务日志的形式写入到本地磁盘中去,并且在成功写入后反馈给Leader服务器一个Ack响应。当Leader服务器接收到超过半数Follower的Ack响应后,就会广播一个Commit消息给所有的Follower服务器以通知其进行事务提交,同时Leader自身也会完成对事务的提交,而每一个Follower服务器在接收到Commit消息后,也会完成对事务的提交。
    3. 奔溃恢复:在正常情况下运行非常良好,但是一旦Leader服务器出现崩溃,或者说由于网络原因导致Leader服务器失去了与过半Follower的联系,那么就会进入崩溃恢复模式。

      1. leader服务器将事物发出后崩溃,ZAB协议就需要确保事务最终能够在所有的服务器上都被提交成功,否则将出现不一致。

      2. leader服务器提出事物后(未发出)崩溃,需要丢弃该条事物

        1. 如果让Leader选举算法能够保证新选举出来的Leader服务器拥有集群中所有机器最高编号(即ZXID最大)的事务Proposal,那么就可以保证这个新选举出来的Leader一定具有所有已经提交的提案。
    4. 数据同步:完成Leader选举之后,在正式开始工作(即接收客户端的事务请求,然后提出新的提案)之前,Leader服务器会首先确认事务日志中的所有Proposal是否都已经被集群中过半的机器提交了,即是否完成数据同步。

      1. Leader服务器会为每一个Follower服务器都准备一个队列,并将那些没有被各Follower服务器同步的事务以Proposal消息的形式逐个发送给Follower服务器,并在每一个Proposal消息后面紧接着再发送一个Commit消息,以表示该事务已经被提交。
  4. Leader选举

    1. 概念:

      1. SID:服务器id(不可重复)
      2. ZXID:事物Id
    2. Leader选举: 当ZooKeeper集群中的一台服务器出现以下两种情况之一时,就会开始进入Leader选举。

      1. 服务器初始化启动。
      2. 服务器运行期间无法和Leader保持连接。
    3. 过程

      1. 第一次投票

        1. 在这个投票消息中包含了两个最基本的信息:所推举的服务器的SID和ZXID,分别表示了被推举服务器的唯一标识和事务ID。下文中我们将以“(SID,ZXID)”这样的形式来标识一次投票信息。举例来说,如果当前服务器要推举SID为1、ZXID为8的服务器成为Leader,那么它的这次投票信息可以表示为(1,8)。
        2. 在第一次投票的时候,由于还无法检测到集群中其他机器的状态信息,因此每台机器都是将自己作为被推举的对象来进行投票。
    4. 变更投票

        1. 规则:根据接受到的投票,如果ZXID比自己的大,就认可当前收到的投票,并再次将该投票发送出去;
        2. 如果ZXID比自己小,那么就坚持自己的投票,不做任何变更;
        3. 如果ZXID和自己的相等,则比较SID大小,规则和比较ZXID相同
    5. 确定Leader

      1. 如果收到半数以上的投票机器,确定为Leader
      2. 简单地说,通常哪台服务器上的数据越新,那么越有可能成为Leader,原因很简单,数据越新,那么它的ZXID也就越大,也就越能够保证数据的恢复。当然,如果集群中有几个服务器具有相同的ZXID,那么SID较大的那台服务器成为Leader。
  1. 应用场景

    1. 数据发布/订阅:

      1. 发布/订阅系统一般有两种设计模式,分别是推(Push)模式和拉(Pull)模式。在推模式中,服务端主动将数据更新发送给所有订阅的客户端;而拉模式则是由客户端主动发起请求来获取最新数据,通常客户端都采用定时进行轮询拉取的方式。
      2. ZooKeeper采用的是推拉相结合的方式:客户端向服务端注册自己需要关注的节点,一旦该节点的数据发生变更,那么服务端就会向相应的客户端发送Watcher事件通知,客户端接收到这个消息通知之后,需要主动到服务端获取最新的数据。
    2. 命名服务

      1. 创建顺序节点,会生成递增编号
    3. 配置中心

      1. 依赖watcher机制可以实时监听数据变化
    4. 注册中心

      1. rpc服务注册中心
    5. 分布式锁

    6. 集群管理

      1. 机器上下线监听

      2. 分布式日志收集系统

        1. 启动注册日志收集机器,并监听
        2. 任务中心根据机器分配任务
        3. 日志收集机器实时上报任务执行进度
    7. Mater选主

      1. 即ZooKeeper将会保证客户端无法重复创建一个已经存在的数据节点。也就是说,如果同时有多个客户端请求创建同一个节点,那么最终一定只有一个客户端请求能够创建成功。利用这个特性,就能很容易地在分布式环境中进行Master选举了。
      2. 监听master节点,挂了再重新选举