CAP理论
CAP理论是分布式系统设计中的一个重要概念,它说明了在分布式环境中三个关键特性——一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)——之间的权衡关系。
一致性
一致性指的是所有节点在同一时刻看到的数据是一致的。在分布式系统中,这意味着当一个写操作在某个节点上成功完成后,所有后续的读操作都应该返回这个新写入的数据。一致性分为强一致性、弱一致性和最终一致性。强一致性要求系统中的所有节点在任何时候都能访问到最新的数据。
强一致性
强一致性是最严格的一致性模型,它要求一旦某个写操作成功返回,所有后续的读操作(无论发生在哪个节点上)都必须返回这个写操作的结果。换句话说,系统中的所有节点在任何时候都能访问到最新的数据。实现强一致性的系统通常采用以下机制:
- 同步复制:在数据写入主节点后,系统会等待所有副本节点也完成数据的复制,然后才确认写操作成功。
- 锁机制:在数据被更新时,系统可能会使用锁来阻止其他操作的执行,直到数据更新在所有节点上完成。 强一致性的优点是简化了应用开发,因为开发者可以假设系统中的数据总是最新的。然而,强一致性会牺牲一定的可用性和分区容错性,因为系统需要在所有副本之间同步数据,这在网络延迟或分区发生时可能导致性能下降或服务中断。
弱一致性
弱一致性模型相对于强一致性来说,放宽了对数据一致性的要求。在弱一致性模型中,写操作成功后,后续的读操作可能会返回旧数据,也可能返回新数据,但系统不保证何时能够访问到最新数据。弱一致性的实现通常不需要严格的同步机制,因此可以在网络延迟或分区发生时提供更高的可用性。弱一致性模型适用于那些对数据实时性要求不高的应用。
最终一致性
最终一致性是弱一致性的一种特殊形式,它保证在没有后续写操作的情况下,经过一段时间,所有节点的数据最终会达到一致状态。这个“一段时间”可能很短,也可能很长,取决于系统的具体实现和网络条件。最终一致性的系统通常采用以下机制:
- 异步复制:数据在主节点更新后,副本节点的更新操作是异步进行的,不需要等待所有副本确认。
- 时间戳:系统可能会使用时间戳来标识数据的版本,以确保最终所有节点都能更新到最新版本。 最终一致性的优点是提供了更高的可用性和分区容错性,因为它允许系统在网络分区或延迟情况下继续运行。但是,它要求应用开发者能够处理数据不一致的情况,这可能增加了应用开发的复杂性。
可用性
可用性指的是系统在面对用户请求时能够持续地提供响应的能力。一个高可用性的系统即使在部分节点或服务出现故障的情况下,也能够保证其他部分正常运行,并且能够处理用户的请求。
在实际应用中,不同的业务场景对可用性的要求不同。例如,对于在线交易系统,高可用性是至关重要的,而对于一些内容发布平台,短暂的不可用可能是可以接受的。
关键要素
- 故障容忍:系统设计时要考虑到各种可能的故障情况,包括硬件故障、软件故障、网络故障等,并能够在这些故障发生时继续运行。
- 快速恢复:当系统中的某个部分发生故障时,系统应该能够快速检测到故障,并采取措施恢复服务,比如通过故障转移(failover)到健康的节点。
- 负载均衡:通过负载均衡技术,系统可以将请求分散到多个节点,避免单个节点过载,从而提高整体系统的可用性。
- 冗余:系统设计中通常会包含冗余组件,如多个副本节点,这样当一个节点失效时,其他节点可以接管其工作。
实现可用性的策略
- 故障检测:系统需要有能力检测到故障的发生,这通常通过心跳检测、健康检查等方式实现。
- 故障转移:当检测到故障时,系统需要能够将故障节点的任务转移到其他健康的节点上,这可以通过主从复制、多主复制等技术实现。
- 数据备份和恢复:定期备份数据,并在需要时能够快速恢复,是确保系统可用性的重要措施。
- 限流和降级:在系统负载过高时,通过限流可以防止系统过载,而降级服务则可以在保证核心功能可用的情况下,牺牲一些非核心功能。
- 自动化运维:自动化部署、监控和故障处理可以大大减少人为错误,提高系统的可用性。
分区容错性
分区是什么
在分布式系统中,网络分区指的是系统中的节点被划分为多个不相交的集合,这些集合中的节点之间无法进行有效通信。分区可能是由于网络故障、延迟、节点故障或配置错误等原因造成的。分区会导致原本统一的系统被分割成多个独立的子系统,这些子系统之间无法交换信息。
分区容错性是什么
分区容错性是指系统在面对网络分区(即系统中的一部分节点无法与其他节点通信)时仍能继续正常运行的能力。在分布式系统中,网络分区是不可避免的,因此系统必须设计成能够在分区发生时继续提供服务。分区容错性的重要性体现在以下几个方面:
- 可靠性:系统在面对网络问题时仍能保持运行,不会因为网络分区而完全失效。
- 稳定性:即使在部分节点无法通信的情况下,系统也能够处理请求,维护服务的稳定性。
- 用户体验:分区容错性好的系统即使在网络问题发生时也能提供尽可能完整的服务,从而提高用户体验。
实现分区容错性的策略
- 数据复制:通过在不同节点上存储数据的多个副本,即使某些节点发生分区,其他节点上的副本仍然可以提供服务。
- 故障转移:当系统检测到某个节点或一组节点发生分区时,可以自动将请求转移到其他健康的节点。
- 去中心化:通过去中心化的设计,减少对单个节点或中心的依赖,使得系统在部分节点失效时仍然能够运行。
- 分布式共识算法:如Paxos、Raft等算法,可以在节点之间达成共识,即使在分区的情况下也能保持系统的一致性。
CAP权衡策略
CAP定理指出,在一个分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)这三个特性不可能同时完全满足。
这意味着在设计分布式系统时,必须在这三个特性之间做出权衡。
CP(一致性 + 分区容错性)
系统选择保证一致性和分区容错性,可能会牺牲可用性。在发生分区时,为了保证数据一致性,系统可能会拒绝某些请求或延迟响应。
AP(可用性 + 分区容错性)
系统选择保证可用性和分区容错性,可能会牺牲一致性。在发生分区时,系统仍然可以响应用户请求,但返回的数据可能不是最新的。
CA(一致性 + 可用性)
在一个没有分区或分区很少发生的系统中,可以同时保证一致性和可用性。然而,在现实世界的分布式系统中,分区是不可避免的,因此这种选择在理论上可行,但在实践中很难实现。
BASE理论
BASE理论是在数据库领域,特别是在分布式系统设计中提出的一种理论,它是对CAP定理的补充和实际应用的一种指导原则。
BASE理论的全称是Basically Available, Soft state, Eventual consistency,即基本可用、软状态、最终一致性。
BASE理论的核心思想是,在分布式系统中,由于网络延迟、分区故障等原因,不可能同时保证强一致性和高可用性,因此可以适当放松一致性要求,允许系统在短时间内不一致,但是最终要达到一致性,同时保证系统的基本可用。
这个理论对于设计大型分布式系统非常有用,特别是在云计算和大数据领域。例如,Amazon的Dynamo数据库、Apache Cassandra和Redis等分布式数据库就是基于BASE理论设计的,它们牺牲了强一致性,换取了更高的可用性和扩展性。
BASE理论的应用允许系统在面对网络分区、节点故障等不可靠因素时,仍然能够提供良好的服务,这对于构建可伸缩的、容错的分布式系统至关重要。
基本可用(Basically Available)
系统在大部分时间内都是可用的,允许系统在一定程度上的不可用。这意味着在分布式系统中,允许分区故障导致部分系统不可用,但是系统整体还是可用的。例如,一个网站可能在某些时候对某些用户不可用,但是大部分用户还是可以正常访问。
软状态(Soft state)
软状态指的是系统中的数据不需要时刻保持一致,可以存在中间状态,这个中间状态不会影响系统的整体可用性。在分布式系统中,数据副本之间的不一致是允许的,并且这种不一致是可以被逐渐修复的。
最终一致性(Eventual consistency)
最终一致性意味着系统中的所有数据副本最终会达到一致状态,但不需要实时一致。在数据更新后,系统的不同部分可能会暂时看到不同的值,但经过一段时间后,这些不一致最终会消失,所有副本都会收敛到同一个值。
分布式事务
分布式事务是指在分布式系统中,涉及到多个节点(通常是多个数据库或者服务)的操作序列,这些操作要么全部成功,要么全部失败,以保持系统数据的一致性。分布式事务的挑战在于系统需要跨网络、跨多个独立操作单元来维护事务的原子性、一致性、隔离性和持久性(ACID属性)。
分布式事务解决方案
两阶段提交(2PC)
两阶段提交(2PC)是一种经典的分布式事务处理协议,用于在分布式系统中确保多个节点上的操作要么全部成功,要么全部失败,从而保持数据的一致性。以下是两阶段提交协议的详细说明:
准备阶段(Prepare Phase)
- 协调者请求:
- 当一个分布式事务需要跨多个节点(参与者)执行时,协调者会向所有参与者发送一个准备(Prepare)请求。
- 准备请求通常包含事务标识符(事务ID)和要执行的操作。
- 参与者投票:
- 每个参与者接收到准备请求后,会执行以下步骤:
- 执行事务:执行事务中的操作,但暂时不提交。
- 记录日志:在本地日志中记录事务的状态,以便在必要时进行回滚或提交。
- 响应协调者:向协调者发送“准备”或“拒绝”的响应。如果参与者能够成功执行事务,它会发送“准备”响应;如果执行失败,则发送“拒绝”响应。
- 每个参与者接收到准备请求后,会执行以下步骤:
提交阶段(Commit Phase)
- 协调者决策:
- 协调者收集所有参与者的响应。如果所有参与者都发送了“准备”响应,协调者会决定提交事务。
- 如果任何一个参与者发送了“拒绝”响应,协调者会决定回滚事务。
- 提交或回滚:
- 提交事务:如果协调者决定提交,它会向所有参与者发送提交(Commit)请求。参与者接收到提交请求后,会正式提交事务,并释放所有持有的资源。
- 回滚事务:如果协调者决定回滚,它会向所有参与者发送回滚(Rollback)请求。参与者接收到回滚请求后,会根据之前的日志记录回滚事务,并释放资源。
特点
- 原子性:2PC确保了分布式事务的原子性,即所有参与者要么全部提交,要么全部回滚。
- 一致性:通过协调者的控制,2PC保证了分布式系统的一致性。
- 单点故障:协调者是一个单点,如果协调者发生故障,可能会导致整个系统无法继续操作。
- 阻塞:在提交阶段,参与者需要等待协调者的决策,这可能会导致阻塞。
缺点
- 性能开销:2PC需要多次网络往返,这会增加事务处理的延迟。
- 协调者故障:如果协调者在准备阶段后故障,参与者可能会长时间处于不确定状态(悬挂事务)。
- 同步阻塞:2PC是同步操作,参与者在提交阶段需要等待,这可能导致资源长时间被锁定。 尽管存在一些缺点,2PC仍然是在分布式系统中实现强一致事务的常用方法。在实际应用中,通常会结合其他技术或策略来优化2PC的性能和可靠性。
三阶段提交(3PC)
三阶段提交(3PC)是对两阶段提交(2PC)的改进,旨在提高分布式事务的可靠性和容错性。3PC通过引入额外的准备阶段来减少参与者的阻塞时间,并增加了一些额外的机制来处理协调者故障的情况。
第一阶段:CanCommit
- 协调者请求:
- 协调者向所有参与者发送CanCommit请求,询问它们是否可以提交事务。
- 参与者投票:
- 参与者收到CanCommit请求后,会评估自身状态,决定是否可以提交事务。
- 如果参与者可以提交事务,它会回复“同意”(Yes)。
- 如果参与者无法提交事务,它会回复“拒绝”(No)。
第二阶段:PreCommit
- 协调者决策:
- 如果所有参与者都回复“同意”,协调者会向所有参与者发送PreCommit请求,指示它们准备提交事务。
- 如果任何一个参与者回复“拒绝”,协调者会向所有参与者发送Abort请求,指示它们回滚事务。
- 参与者准备提交:
- 参与者收到PreCommit请求后,会执行以下操作:
- 执行事务:执行事务中的操作,但暂时不提交。
- 记录事务状态:在本地日志中记录事务的状态,以便在必要时进行回滚或提交。
- 回复协调者:向协调者发送“PreCommit确认”响应。
- 参与者收到PreCommit请求后,会执行以下操作:
第三阶段:DoCommit
- 协调者提交:
- 如果协调者从所有参与者那里收到了“PreCommit确认”响应,它会向所有参与者发送DoCommit请求,指示它们提交事务。
- 如果协调者在PreCommit阶段后丢失了某些参与者的响应,它会等待一定时间,然后重试PreCommit或Abort请求。
- 参与者提交或回滚:
- 提交事务:参与者收到DoCommit请求后,会正式提交事务,并释放所有持有的资源。
- 超时机制:如果参与者在一定时间内没有收到DoCommit请求,它会假设协调者已经故障,并且会自主决定提交事务。这是3PC与2PC的一个重要区别,3PC允许参与者自行决定提交,以避免长期阻塞。
特点
- 容错性:3PC在协调者故障的情况下,参与者可以自主决定提交事务,减少了系统阻塞。
- 减少阻塞:通过引入CanCommit阶段,3PC减少了参与者在PreCommit阶段的阻塞时间。
- 非完全原子性:在某些情况下,3PC可能无法保证所有参与者都提交或回滚,这可能导致部分事务提交。
缺点
- 网络开销:3PC需要更多的网络往返,可能会增加事务处理的延迟。
- 数据一致性:在极端情况下,3PC可能无法保证所有参与者的一致性,尤其是在网络分区的情况下。
尽管3PC比2PC更复杂,但它提供了更好的容错性和减少了一些阻塞问题。然而,3PC仍然不是万能的,它也有自己的局限性,特别是在网络分区和参与者故障的情况下。在实际应用中,选择分布式事务协议时,需要根据具体的应用场景和系统需求来做出决策。
补偿事务(TCC - Try-Confirm-Cancel)
- Try:执行业务操作,预留必要的资源,但不提交事务。
- Confirm:如果所有服务都成功执行了Try操作,则执行Confirm操作,正式提交事务。
- Cancel:如果有服务执行Try操作失败,则执行Cancel操作,回滚事务。
Saga模式
- Saga由一系列本地事务组成,每个本地事务更新数据并发布事件或消息。
- 如果某个本地事务失败,Saga执行一系列补偿事务来回滚之前的事务。
本地消息表(Local Message Table)
- 在本地数据库中维护一个消息表,用于记录事务的执行状态。
- 通过消息的发送和接收来保证事务的最终一致性。
消息队列(Message Queue)
- 使用消息队列来异步处理跨服务的事务操作。
- 通过消息的确认机制来保证事务的最终一致性。
事务消息(Transaction Message)
- 结合消息队列和数据库事务的特性,确保消息的发送和数据库操作的原子性。
- 消息队列支持事务消息的发送,只有当数据库事务提交后,消息才会被投递。
AT模式(Automatic Transaction)
- 基于Seata等分布式事务框架,自动在业务代码中加入数据操作的补偿逻辑。
- 通过框架自动处理分支事务的提交和回滚。
最大努力通知(Best Effort Notification)
- 服务在完成本地事务后,尝试通知其他服务,但不保证通知一定成功。
- 其他服务通过定时任务或补偿机制来处理可能的通知失败。
可靠事件模式(Reliable Event)
- 事件的发布和消费都是可靠性的,确保事件最终会被正确处理。
- 通常结合事件日志和补偿事务来实现。
分布式一致性
Paxos
在深入了解Paxos算法之前,我们先来认识一下该算法中的三个关键角色:
- Proposer(提议者):负责发起提案,试图让系统中的节点就某个值达成共识。
- Acceptor(接受者):负责接收提议者的提案,并在满足条件的情况下承诺接受这些提案。
- Learner(学习者):从接受者那里获取已达成共识的值,并将其应用于系统的状态更新。
Paxos算法的主要流程
阶段一:准备(Prepare)
- Proposer选定一个独一无二的提案编号
,并向所有Acceptor广播一个prepare
请求,附带该编号。此阶段请求中无需包含具体的提案值。- 提案编号用于确保提案的顺序性和唯一性,避免冲突。
- 通常,Proposer会选择一个比之前所有提案编号都大的值。
- 当Acceptor接收到
prepare
请求后,会执行以下操作:- 如果Acceptor之前没有承诺过编号大于或等于
的提案,则它会回应Proposer,包含它最后一次承诺的提案编号及其值(如果有)。 - 如果Acceptor已经承诺过编号大于
的提案,则它会忽略这个prepare
请求。
- 如果Acceptor之前没有承诺过编号大于或等于
- Proposer等待,直到收到来自多数Acceptor的响应,然后进入第二阶段。
阶段二:接受(Accept)
- Proposer根据收到的响应来确定要提议的值
:- 如果所有响应中都没有包含提案值,Proposer可以自由选择一个值。
- 如果有响应包含了提案值,Proposer必须选择编号最大的提案对应的值。
- Proposer向多数Acceptor发送一个包含提案编号
和提案值 的accept
请求。 - 当Acceptor接收到
accept
请求后,会有以下两种处理方式:- 如果Acceptor尚未承诺过编号大于
的提案,它会接受这个提案,并记录下提案编号 和值 。 - 如果Acceptor已经承诺过编号大于
的提案,它会忽略这个accept
请求。
- 如果Acceptor尚未承诺过编号大于
- Proposer等待,直到收到多数Acceptor的确认,此时提案
被视为已被接受。
阶段三:学习(Learn)
- 当提案
被多数Acceptor接受后,Learner可以认为这个值已经达成共识,并将其应用于系统状态。- Learner可以通过监听Acceptor的接受消息或者通过专门的通信渠道来获知这一信息。
- 一旦提案被接受,它就成为了系统的一致性决策。
Paxos算法的优势
- 安全性保障:一旦某个值被接受,就不会再被更改,确保了系统状态的一致性。
- 活性保障:只要大多数节点保持活跃,Paxos算法就能持续运行,不会陷入僵局。
- 容错能力:即使部分节点失效或网络出现延迟,只要超过一半的节点能够正常通信,系统仍能正确执行Paxos算法。
Raft
Raft算法是一种实现分布式系统的领导者选举和日志复制的一致性算法,由Diego Ongaro和John Ousterhout在2013年提出。
与Paxos算法相比,Raft算法更易于理解和实现。
Raft算法将一致性问题分解为几个主要的子问题:领导选举、日志复制、安全性和变更配置。
节点角色
- Follower:被动角色,响应来自领导者或候选者的消息。
- Candidate:当 Follower 在一定时间内没有收到领导者的消息时,它会转变为 Candidate 并开始选举过程。
- Leader:负责处理所有客户端请求,并向其他服务器发送心跳信息以保持其领导地位。每次成功的选举后都会有一个新的 Leader。
任期
在Raft中,任期的概念类似于逻辑时间戳,它是一个连续递增的整数。每当发生领导选举时,任期就会增加。每个任期开始时,都会尝试选举出一个新的领导者。任期的作用:
- 区分领导者的有效性:任期用于区分不同领导者的任期,确保系统的状态不会因为旧的领导者而变得混乱。如果一个节点收到了来自一个任期较低的领导者的消息,它会忽略这个消息,因为它知道有更高任期的领导者存在。
- 选举的唯一性:每个任期只能选举出一个领导者。如果一个领导者因为网络分区或故障而失去了与集群的联系,新的任期将会开始,并选举出新的领导者。
- 日志条目的版本控制:任期还被用来保证日志的一致性。每个日志条目都关联着一个任期号,这有助于确定哪些日志条目已经被提交,并且帮助解决不同服务器之间日志不一致的问题。
超时设置
Leader 通过定期向所有 Follower 发送心跳消息来维持其领导地位。心跳消息包含 Leader 当前的任期号,这样可以防止 Follower 因超时而转变为 Candidate 并触发新的选举。
超时设置有助于检测 Leader 的失败,并且能够触发新的选举过程。主要的超时设置包括选举超时和心跳间隔。
选举超时:
选举超时是指 Follower 在没有收到 Leader 发送的心跳或 AppendEntries RPC 消息后等待的时间长度。如果在这个时间内没有收到任何消息,Follower 将认为当前 Leader 已经失效,并转换为 Candidate 开始新一轮的选举。为了避免多个节点同时成为 Candidate 并引发分裂投票(Split Vote),每个 Follower 在启动时都会设置一个随机的选举超时时间。通常这个时间范围设定在 150ms 到 300ms 之间。
心跳间隔:
心跳间隔是指 Leader 向所有 Follower 发送心跳消息的频率。Leader 会定期发送空的日志条目或心跳消息给 Follower,以表明自己仍然活跃并且是合法的 Leader。
心跳间隔通常比选举超时要短得多,一般情况下可能设置为几十毫秒到几百毫秒不等。这样做的目的是确保即使在网络条件不佳的情况下,也能频繁地向 Follower 发送心跳消息。
领导选举
选举触发
当Follower在选举超时时间内没有收到来自Leader的心跳消息时,它会认为Leader已经宕机或网络出现了问题,因此会触发选举过程。
每个Follower都有自己的选举超时计时器,超时时间通常是随机化的,以减少多个节点同时成为Candidate并触发选举的可能性。
选举过程
- 转换为Candidate:
- Follower在选举超时后,会将自己的状态转换为Candidate,并开始一轮新的选举任期(Term)。
- 增加任期号:
- Candidate会增加自己的当前任期号,并在发起选举时使用这个新的任期号。
- 请求投票(RequestVote RPC):
- Candidate会向集群中的其他节点发送RequestVote RPC消息,请求它们投票给自己成为新的Leader。
- RequestVote RPC 包含候选人的任期号、候选人最后一条日志条目的索引位置及其任期号、要求投票的请求等信息。
- 投票规则:
- 每个节点在一个任期内只能投票一次,通常会投票给第一个请求投票的Candidate,这个规则有助于减少选举冲突。
- 节点只有在以下情况下才会投票给Candidate:如果Candidate的日志至少和自己一样新(即Candidate的日志至少和自己的一样完整)。
- 如果Candidate的任期号大于接收者节点当前的任期号,那么接收者会更新自己的任期号,并且如果在这个任期内还没有投票给其他的 Candidate,它会投票给这个 Candidate。
- 选举结果:
- 如果一个Candidate从集群中的大多数节点获得了投票,它就会成为新的Leader。
- 成为Leader后,它会立即向所有其他节点发送心跳消息(通常是空的日志条目形式的 AppendEntries RPC),以确立其领导地位并阻止新的选举。
- 处理多个Candidate:
- 如果有多个节点同时成为Candidate,可能会发生分裂投票(Split Vote),此时没有任何Candidate能够获得多数票。
- 在这种情况下,每个Candidate的选举超时后会再次尝试发起选举,直到有一个Candidate能够获得多数票。
选举后的操作
- Leader确认:
- 新选举出的Leader会开始发送心跳消息,以维护其领导地位,并处理来自客户端的请求。
- 日志复制:
- Leader会开始复制其日志条目到所有Follower,以确保集群状态的一致性。
- 处理遗留的Candidate:
- 在选举过程中,可能会有一些Candidate没有赢得选举。当它们收到来自新Leader的心跳消息时,会自动降级为Follower。
日志复制
一旦选举出领导者,它就开始处理来自客户端的操作请求。
- 客户端请求:客户端发送请求给领导者。
- 日志追加:领导者将客户端请求作为新的日志条目追加到自己的日志中。
- 日志复制:领导者并行地将新条目发送给所有跟随者。跟随者将条目追加到自己的日志中,并回应领导者。
- 提交条目:一旦领导者收到多数(比如超过半数)跟随者的确认,它就会将条目标记为已提交,并将结果返回给客户端。
- 跟随者同步:领导者通过后续的心跳消息通知跟随者哪些条目已被提交,跟随者随后将这些条目应用到自己的状态机。
安全性
选举限制
Raft算法通过选举限制来确保只有拥有最新日志信息的节点才能成为领导者。
- 日志条目完整性:在Raft中,一个节点要想成为领导者,它必须拥有所有已提交日志条目的信息。这是通过比较日志条目的最后索引和任期来实现的。如果一个候选人的日志不如其他节点的新,那么它将无法赢得选举。
- 预防旧领导回归:如果一个节点因为网络分区而失去了领导地位,它的日志可能会落后于其他节点。当网络恢复时,这个节点不能立即成为领导者,因为它可能缺少一些已经提交的日志条目。这种机制防止了旧领导者的数据覆盖新领导者的数据。
日志匹配属性
Raft算法保证了日志匹配属性,这意味着如果两个日志条目在相同的日志位置具有相同的任期编号,那么它们之前的所有日志条目都是相同的。
- 日志复制:领导者将日志条目复制到跟随者节点,并且保证日志条目的顺序。如果跟随者与领导者的日志在某些位置不匹配,领导者会强制跟随者的日志与自己的日志一致。
- 冲突解决:在日志复制过程中,如果发现冲突(即不同的日志条目出现在相同的日志位置),领导者会覆盖跟随者的冲突条目。这是因为领导者拥有最新的已提交日志条目。
提交之前的日志条目
Raft算法确保了在领导者提交一个日志条目之前,该条目必须已经被复制到多数服务器上。
- 多数同意:一个日志条目在被领导者认为是已提交之前,必须被多数节点接受。这意味着即使领导者崩溃,这个条目也不会丢失,因为它已经被复制到了其他节点。
- 防止数据丢失:通过要求多数节点复制日志条目,Raft算法确保了即使在发生网络分区或节点故障的情况下,已提交的数据也不会丢失。
变更配置
Raft算法的配置变更机制允许在不中断服务的情况下添加或移除服务器,这对于分布式系统的可伸缩性和容错性至关重要。
单步变更
- 提交配置变更请求:
- 客户端发起一个配置变更请求,该请求被Leader作为一条特殊的日志条目追加到自己的日志中。
- 这条日志条目包含了新的服务器配置信息。
- 复制日志条目:
- Leader将包含配置变更的日志条目复制到所有当前配置中的服务器上。
- 为了确保变更的安全性,这条日志条目必须被复制到多数服务器上,并且被提交。
- 应用配置变更:
- 一旦配置变更的日志条目被提交,新的配置立即生效。
- 所有服务器根据新的配置开始工作,如果配置中添加了新服务器,Leader将开始向它们复制日志条目;如果配置中移除了服务器,Leader将停止向这些服务器发送日志条目。
单步变更的缺点是,如果在配置变更过程中Leader失效,新的Leader可能仍然使用旧的配置,这可能导致数据不一致。因此,更安全的方法是使用两阶段变更。
两阶段变更
- 提交中间配置:
- 首先,将当前的配置和新的配置合并成一个中间配置,并将其作为日志条目追加到Leader的日志中。
- 这个中间配置包含了所有旧的服务器和新的服务器。
- 复制并提交中间配置:
- Leader将中间配置的日志条目复制到所有服务器上,并等待它被提交。
- 一旦中间配置被提交,系统进入一个过渡状态,在这个状态下,新旧配置都有效。
- 提交最终配置:
- 接着,Leader创建一个新的日志条目,只包含新的配置,并将其复制到所有服务器上(包括新加入的服务器)。
- 当这个最终配置的日志条目被提交后,旧的配置被完全替换,新的配置成为唯一有效的配置。
两阶段变更的关键优势在于它减少了在配置变更过程中出现数据不一致的风险。如果在变更过程中Leader失效,由于中间配置包含了所有服务器,新的Leader仍然可以与多数服务器通信,从而保证系统的稳定性。