0、前言
【分布式事务】因为是【跨库事务】,在一次业务操作中,必须保证所有参与的数据库物理节点上的事务都【提交】或【回滚】。
单体数据库很容易满足事务ACID特性,提供【强一致性】保证,但是【分布式事务】要完全遵循ACID比较困难。【强一致性】的事务称为【刚性事务】;把提供【最终一致性】的事务称为【柔性事务】。
而为了保证【分布式事务】也能满足ACID特性,相关组织或者个人提出了一系列的实现【分布式事务】的理论、协议(有些被称作 共识协议)和算法。
1、CAP和BASE
1-1、CAP
对于一个分布式系统,或者分布式数据存储系统而言,一致性(Consistency)、可用性(Availability)和分区耐受性(Partition tolerance) 三者 不能同时满足;
维基百科关于CAP的说明:
it is impossible for a distributed data store to simultaneously provide more than two out of the following three guarantees:
- Consistency: Every read receives the most recent write or an error
- Availability: Every request receives a (non-error) response, without the guarantee that it contains the most recent write
- Partition tolerance: The system continues to operate despite an arbitrary number of messages being dropped (or delayed) by the network between nodes
1、一致性(Consistency)
每次读取的数据都应该是【最近写入】的数据或者返回一个错误(Every read receives the most recent write or an error),而不是过期的数据,也就是说,数据是一致的;
在【分布式环境】中,数据在【多个副本之间】【保持一致】;对于将数据副本保存在不同分布式节点的系统,如果对第一个节点的数据进行更新并且更新成功后,却没有更新第二个节点上的数据,就是典型的【分布式不一致】情况;
这里对【强一致性】做出说明: 在分布式系统中,如果能够做到针对一个数据项的更新操作成功后,所有的用户都可以读取到最新的值;
2、可用性(Availability)
每次请求都应该得到一个响应,而不是返回一个错误或者失去响应,不过这个响应结果不需要保证数据时最近写入的(Every request receives a (non-error) response, without the guarantee that it contains the most recent write),也就是说系统一直都是正常使用,不会返回调用者异常,但是并不保证响应的数据时最新的;
**3、**分区耐受性(Partition tolerance)
即使因为网络原因,部分服务器节点之间消息丢失或者延迟,系统依然可以操作(The system continues to operate despite an arbitrary number of messages being dropped or delayed by the network between nodes);
1-2、BASE
【BASE】是对【CAP】中【一致性】和【可用性】权衡的结果,是基于【CAP理论】逐步演化而来,其核心思想是即使无法做到【强一致性(Strong consistency)】,但每个应用都可以根据自身业务特点,采用适当的方式来使系统达到【最终一致性(Eventual consistency)】。
【BASE】是 **Basically Available(基本可用)、Soft state(软状态)和 Eventually consistency(最终一致性)**三个短语的缩写。
1-2-1、基本可用 (Base Available)
分布式系统在出现不可预知故障的时候,允许损失【部分可用性】,但不等价于系统不可用。
典型的例子:
响应时间上的损失:正常情况,一个在线搜索引擎要做0.5秒内返回给用户查询结果,但是由于系统部分机房发生断电或网络故障,查询结果的【响应时间】增加的1~2秒;
功能上的损失:正常情况下,在一个电子商务网站购物,消费者能够顺利完成每一笔订单,但是在大促高峰期间,由于消费者购物行为激增,为了保护系统的稳定性,部分消费者可能会被引导到一个降级页面;
1-2-2、软状态(Soft State)
允许系统中的数据存在【中间状态】,并认为该【中间状态】的存在不会影响系统的整体可用性,即允许系统在不同节点的数据副本之间进行【数据同步】的过程存在【延时】。
1-2-3、最终一致性(Eventual Consistency)
【最终一致性】强调的是系统中所有数据副本,经过一段时间同步后,最终能够达到一致性状态。【最终一致性】的本质是【需要系统保证最终数据能够达到一致】,而不需要实时保证系统数据的【强一致性】。
2、两阶段提交(Tow-phase commit)协议
来自维基百科:en.wikipedia.org/wiki/Two-ph…
In transaction processing, databases, and computer networking, the two-phase commit protocol (2PC) is a type of atomic commitment protocol (ACP). It is a distributed algorithm that coordinates all the processes that participate in a distributed atomic transaction on whether to commit or abort (roll back) the transaction (it is a specialized type of consensus protocol).
【两阶段提交】是一个原子提交协议;【两阶段提交】是一个【分布式算法】,用于协调参与【分布式原子事务】的所有进程是否【提交】或【回滚】事务。
2-1、协调者和参与者
两阶段提交中,有两个角色:
-
协调者(coordinator):负责向各个【参与者】发送事务提交操作,根据参与者反馈的结果来完成事务,在实现上,可以看做一个【事务管理程序】,记录了要参与事务的数据库和参与操作的数据;
-
参与者(participants):负责执行【提交】或【回滚】事务的节点,无论【提交】或【回滚】,都需要将执行结果(agreement 或 abort)回馈给【协调者】,这里在实现上可以看做一个数据库管理系统,负责提交或回滚事务,并返回执行结果;
2-2、算法详解
所谓两阶段,主要是以下两个阶段:
- 提交请求(commit-request)阶段:也叫投票(voting)阶段,【协调者】向【参与者】发送一个【询问-提交(query to commit)】消息,并且一直等到【所有参与者】都回复消息;
- 提交(commit)阶段:也叫完成(completion)阶段,根据在【提交请求阶段】,各个【参与者】反馈的结果,决定是【提交】还是【回滚】整个事务;
2-2-1、提交-请求(commit-request)阶段
此阶段流程:
- 【协调者】向【参与者】发送<询问-提交>消息,然后一直等待所有的【参与者】的回复;
- 【参与者】收到提交请求后,执行事务;参与者会写入【undo log】或【redo log】;
- 【参与者】回复【协调者】事务请求结果,如果【参与者】任务可以成功提交事务,则返回【同意】消息(投赞同[Yes]票,表示可以提交事务),如果【参与者】认为提交事务会失败,则返回【中止】消息(投反对票[No],表示不能提交事务);
2-2-2、提交/回滚阶段
这个阶段【协调者】会根据【提交-请求】阶段【参与者】的返回结果,确认执行【提交】还是【回滚】操作;
- 如果所有【参与者】都返回【同意】(投赞同票):
- 【协调者】向所有【参与者】发送【commit】消息;
- 各【参与者】完成事务操作(正式commit事务),并且释放在事务处理期间的【锁】和【资源】;
- 【参与者】向【协调者】发送(acknowledgement)消息;
- 【协调者】收到所有的【acknowledgement】消息后完成事务;
- 如果任一(any)【参与者】返回【中止】消息,或者【协调者】等待超时:
- 【协调者】向所有【参与者】发送【rollback】消息;
- 各【参与者】通过undo log 【撤销】事务,并且释放在事务处理期间的【锁】和【资源】;
- 【参与者】向【协调者】发送(acknowledgement)消息;
- 【协调者】收到所有的【acknowledgement】消息后【撤销】事务;
2-3、算法优缺点
2-3-1、优点
原理简单,容易实现。
2-3-2、缺点
两阶段提交是一个【阻塞式】协议,有以下缺点:
- 单点问题,如果【协调者】出现问题,【协调者】无法处理事务,无论是提交还是回滚操作,这样事务执行的资源永远被锁定;
- 数据不一致,在【提交阶段】,部分【参与者】没有收到commit或者rollback请求,导致整体的【分布式事务】出现不一致;
2-4、两阶段提交协议的问题
在【提交】阶段,两阶段提交无法进行可靠恢复,无论是因为【协调者】失败或者【参与者】失败。
如果仅仅是【协调者】失败,并且没有任何【参与者】收到【commit】消息,那么可以判断本次的事务没有提交。
但是如果【协调者】和【参与者】都失败,则有可能部分【参与者】收到通知并提交事务,其它【参与者】没有完成事务提交,这就出现上面提到的【一致性问题】;并且此时所有的【参与者】的资源都是锁定的,还无法释放。
3、三阶段提交(Three-phase commit)
来自维基百科:
The three-phase commit protocol eliminates this problem by 【introducing the Prepared to commit state】.
If the coordinator fails 【before sending preCommit messages】, the cohort will unanimously agree that the operation was aborted.
The coordinator will not send out a doCommit message until all cohort members have ACKed that they are Prepared to commit. This eliminates the possibility that any cohort member actually completed the transaction before all cohort members were aware of the decision to do so
The 【pre-commit phase】 introduced above helps us to recover from the case when a participant failure or both coordinator and participant node failure 【during commit phase】.
When the recovery coordinator takes over after coordinator failure during commit phase of two-phase commit, the new pre-commit comes handy as follows:
On querying participants, if it learns that some nodes are in 【commit phase】 then it assumes that previous coordinator before crashing 【has made the decision to commit】. Hence it can shepherd the protocol to commit.
Similarly, if a participant says that it doesn’t receive 【PrepareToCommit】 message, then the new coordinator can assume that the previous coordinator failed even before it completed the 【PrepareToCommit phase】. Hence it can safely assume no other participant would have committed the changes and hence safely abort the transaction.
针对上面【两阶段提交】引起的【同步阻塞】和【数据不一致】问题,【三阶段提交】引入了【准备提交】状态消除此问题。
仅个人理解:【三阶段提交的真正意义,是加强了二阶段提交,失败后数据恢复的能力,以保证数据的一致性】。
如果【协调者】在发生【preCommit】消息之前就失败,那本次事务操作就中止(abort);
在所有【参与者】都已确认【准备提交】之前,【协调者】不会发出【doCommit】消息。
三阶段提交中,如果【协调者】节点出现问题,【新的协调者】会更加【参与者】当前的【状态】,判断是继续【提交】还是【中断】事务;
4、分布式共识(distributed consensus)
在介绍Paxos和Raft之前,首先了解一下什么是分布式共识。
4-1、什么是共识?
如果只有一个节点,节点作为数据库来存储数据,当客户端提交数据进行修改,单个节点更新数据,数据达成一致,因为只有一个节点,很容易达成共识。
来自维基百科的解释:
The consensus problem requires agreement among a number of processes (or agents) for a single data value.
The consensus problem is a fundamental problem in control of multi-agent systems.
4-2、分布式下多个节点数据共识
当使用【多个节点】进行数据存储,一份数据可以在多台机器上进行拷贝,如何保证多个节点的【数据一致性】,就是分布式共识的问题;
当前解决这个问题比较流行的算法包括 Paxos 和 Raft。
5、Paxos算法
Paxos是一种【共识(consensus)】算法,在【分布式】的应用场景中,理解为多个客户端要【同时】修改【同一个值】,最终由集群投票决定哪个客户端可以修改,投票的基本标准是选择【事务ID最大】的客户端,可以修改那个值。
Paxos的维基百科页:en.wikipedia.org/wiki/Paxos_…
从算法的角度分析Paxos。
5-1、Paxos算法的角色
- Proposer:负责提交一个【提案(Proposal)】;
- Acceptor:有【两个】动作,分别是【批准】和【通过】提案;
- Learner:被动接收方,当【提案】被【通过】后,接收结果;
5-2、关于Paxos中的定义
- Quorums : 全体Acceptor的子集,每两个子集中必须至少共享一个成员;Quorums都要包含多数Acceptor**,Proposer发出的提案都必须给到一个Quorums中;比如有 A、B、C、D四个Acceptor,可以分为{A, B, C},{A, B, D},{A, C, D},{B, C, D} 这些Quorums;**
- Proposal number :每个提案都有一个编号,编号是唯一的;
5-3、Paxos的两阶段
Paxos算法被分为两个阶段,每个阶段也被分层两个部分
5-3-1、阶段一
1、Prepare
一个【Proposer】向多个【Acceptor】(这些Acceptor都在一个Quorums中)发送 消息,消息中【仅包含】「Proposal Number」,如 {P0, };
2、Promise
多个【Acceptor】收到__ 消息,根据消息中的「Proposal Number」,与【Acceptor】自己之前 或者 过的「Proposal Number」比较,如新提交的提案编号为 P0,原有的提案编号为 P1,若 P0 > P1,则【Acceptor】将之前 过的提案{P1, V1}组装为一个 消息 返回给 【Proposer】;若 P0 <= P1,则忽略这次的__;【Acceptor】会保存 过的 最大提案号;
5-3-2、阶段二
1、Accept
当【Proposer】收到一个【Quorums】中的多数(超过一半)的【Acceptor】的 消息 ,则提交 <Accept> 消息给 之前的【Acceptor】,并且把需要提交的值V作为提案值一起提交,如 {P0, V0};
2、Accepted
多个【Acceptor】收到 消息后,如果判断提案中的编号P0大于之前 过的提案号,就像所有 【Leaner】和发送提案的 【Proposer】发送 消息,本轮提案流程结束;【Acceptor】保存 <Accepted> 过的 最大提案号;
5-4、Paxos算法总结
如果Paxos理解成 分布式数据存储系统中 更新一个值,必须获得分布式集群中【一半以上】的节点同意,那么这个值就可以达到分布式一致性;
当一个提案被批准后,Acceptor负责将批准的提案发送给Learner;就像一个数据在分布式集群中被同意更新,需要通知节点更新。
关于 Paxos 的简单总结
-
三种角色:Proposer,Acceprtor,Learner;
-
两步阶段:类似两阶段提交;
-
一条准则:Acceprtor 只对 更大的 提案号 做出动作(如 或 );
6、Raft算法
RAFT算法也是分布式系统中的共识算法。
6-1、RAFT算法中的状态
Leader : 集群中的主节点,负责接收客户端的数据并处理,向_Follower_同步数据并发送心跳;
Follower : 接收来自 Leader 的数据同步和心跳检查,如果心跳超时,则状态改为 Candidate,进行 Leader 选举;
Candidate :竞选 Leader,向多个节点发起投票请求,如果得到集群中超过半数的票,则当选为Leader;
大致的状态转换过程:
6-2、Leader选举_算法简单描述
1、集群刚启动时,所有的节点都处于 Follower ;
2、当 Follower 接收不到心跳,状态转变为 Candidate ;
3、Candidate状态 的节点,向其他节点发出投票请求;
4、节点投票同意,将投票结果返回,如果集群中【超过一半】的节点同意某个 Candidate 的投票,则这个 Candidate 当选为 Leader ,这就是【Leader选举】的过程;
6-3、关于Leader选举方式
Raft 集群维护【两个】【超时设置】来控制 Leader 选举:
-
选举超时,Follower 成为 Candidate 的等待时间;选举超时的时间是在【150ms - 300ms】之间的【随机值】;
-
心跳超时,如果在心跳超时结束前,没有收到【心跳检测】消息,则 Follower 转为 Candidate,再次进行投票竞选 Leader;
6-4、首次Leader选举
1、当集群首次启动,所有节点状态都是 Follower ,开始【选举】Leader,当每个节点的【选举超时】倒计时结束后,节点状态转变为 Candidate;
2、当 Follower 节点【选举超时】时间结束后,状态改为 Candidate,将【任期Term值】改为1,并为自己投票【Vote Count : 1】;
3、Candidate节点 向其他节点发送【Request Vote】消息,进行投票;
4、当收到投票请求的节点,如果从未投过票,则给发起投票的 Candidate 节点投票,会做以下几个动作:
- 将节点的【任期值从0改为1】;
- 【重置】节点的超时时间;
- 将投票结果 返回给 发起投票的 Candidate 节点;
5、Candidate 收到来自集群中大多数(一半以上的节点)的投票后,则成功竞选为 Leader,一次 Leader竞选 完成;
6、Leader节点 开始向 Follower节点 发送【心跳超时】消息;
Follower节点 收到【心跳超时】报文后,会给 Leader节点 进行反馈;
以上就是Raft算法的【Leader选举】过程,一个【选举任期】的结束会持续到一个 _Follower节点_接收不到心跳检测并转变为 Candidate;
6-5、Raft完成共识的过程
Leader选举 成功后,整个系统的所有修改都要通过 Leader,如果下图:
1、客户端需要修改某个值为5,将值传给 Leader 节点;
2、Leader 接收到 客户端的请求,并不会立即修改值,而是将修改请求作为节点日志的一个条目,此时 Leader节点的值并没有被修改;
3、Leader 向所有 Follower 发送请求,Follower收到请求后也在本地记录一条日志;
4、Leader 收到集群中【超过一半的】Follower投票后,更新值,并再向Follower发出通知进行更新,此时集群中的所有节点的数据都一致,达成【共识】;
5、Follower节点 也将值进行更新;
7、Zookeeper的ZAB协议
Zookeeper并没有采用Paxos算法,而是使用一种称为Zookeeper Atomic Broadcast(ZAB,Zookeeper原子消息广播协议)作为其数据一致性的核心算法。
【ZAB协议】是 为 _Zookeeper【专门设计】_的支持【崩溃恢复】的原子广播协议。
在Zookeeper中,主要依赖【ZAB协议】来实现【分布式数据一致性】,Zookeeper基于【ZAB协议】实现一种【主备模式】的系统架构来保持集群中各副本之间数据的一致性。
7-1、Zookeepr中的角色
- Leader :Zookeeper集群中【只有一个】Leader,负责处理客户端事务请求,向 Follower发起投票,并根据投票结果确认是否提交本次事务请求;
- Follower : 对 Leader 发出的请求进行投票,并按照 Leader 提交请求更新数据;
7-2、ZAB协议目的
保证Zookeeper集群中【超过一半】的 Follower服务器 与 Leader服务器 的【数据保持一致】;
7-3、ZAB协议原理
- Leader服务器将客户端事务请求,分发(Propose)给集群中所有的 Follower服务器;
- Follower服务器收到请求后,给 Leader服务器 进行反馈 Ack;
- Leader服务器收到集群中【一半以上】的 Follower服务器 反馈,就向所有的 Follower服务器 发送 Commit消息;
7-4、ZAB协议中的一些补充细节
- ZAB协议需要保证如果一个状态变更已经被处理,所有其依赖的状态变更都已经被提前处理掉,所有的操作必须被顺序执行;
7-5、ZAB协议的两种模式
- 恢复模式
- 消息广播
7-6、恢复模式
ZAB协议 需要保证【主进程】发生 崩溃 或者 重启 等异常情况,依旧能正常工作。
ZAB在几种情况下进入【恢复模式】:
- 整个服务启动过程中;
- Leader服务器出现网络中断、崩溃退出与重启等异常情况;
- 集群中已经存在Leader服务器,新加入的服务器会进入数据恢复模式;
ZAB协议在【Leader服务崩溃】时需要维持的特性:
- 在Leader服务器【已提交】的事务,最终要被所有服务器都提交;
- 在Leader服务器【未提交】的事务,必须丢弃;
7-7、消息广播
ZAB协议【消息广播】的两阶段提交:
- Leader服务器收到客户端发送事务请求,生成事务Proposal,并发送到集群中的所有机器,收集各机器的反馈结果;
- 如果集群【超过一般的】机器反馈,就进行事务提交。