分布式系统
分布式系统是应对海量请求、海量数据存储的核心应用,而分散的多节点如何确保数据的正确性、一致性呢?这也分布式系统要解决的核心问题。
分布式系统中有很多协议算法,不同的应用场景最适用的协议算法可能不同。比如,Paxos 和 Raft 主要用于实现分布式一致性,2PC 和 3PC 用于分布式事务管理,Gossip 协议用于信息传播和故障检测。
再细分,有的是流程严格一致性,有的是弱一致性,接受一定的延迟。这就是分布式系统著名的 CAP 理论。
CAP 理论
CAP 理论是分布式系统中的一个基本概念,由计算机科学家 Eric Brewer 在 2000 年提出。CAP理论指出,在一个分布式系统中,不可能同时满足以下三个特性:
- 一致性(Consistency) :所有节点在同一时间看到的数据是相同的,即每次读操作都能读取到最新的写操作结果。
- 可用性(Availability) :每个请求(无论是读请求还是写请求)都能在合理的时间内得到响应,即系统始终是可用的。
- 分区容忍性(Partition Tolerance) :系统能够继续运行,即使网络分区(网络故障导致节点之间无法通信)发生。
根据 CAP 理论,在一个分布式系统中,最多只能同时满足两个特性,而不能同时满足三个特性。这意味着系统设计者必须在一致性、可用性和分区容忍性之间进行权衡。
CAP理论的三种组合
-
CP(一致性 + 分区容忍性) :
- 系统在网络分区发生时,仍然保证数据的一致性,但可能会牺牲可用性。
- 例如,Zookeeper 和 HBase 在网络分区时会优先保证数据一致性,但可能会导致部分请求无法得到响应。
-
AP(可用性 + 分区容忍性) :
- 系统在网络分区发生时,仍然保证可用性,但可能会牺牲一致性。
- 例如,Cassandra 和 DynamoDB 在网络分区时会优先保证系统的可用性,但可能会导致数据不一致。
-
CA(一致性 + 可用性) :
- 系统在没有网络分区的情况下,能够同时保证一致性和可用性。
- 这种组合在实际的分布式系统中很难实现,因为网络分区是不可避免的。
Redis中的CAP
Redis 是一个开源的内存数据结构存储系统,广泛用于缓存、消息队列和实时分析等场景。Redis 本身并不严格遵循 CAP 理论中的某一类(CP或AP),但其不同的部署模式和配置选项可以使其在一致性和可用性之间进行权衡。
Redis的不同部署模式
-
单节点模式:
- 在单节点模式下,Redis 既可以保证一致性(因为只有一个节点处理所有请求),也可以保证可用性(只要节点不故障)。
- 但单节点模式下没有分区容忍性,因为单节点故障会导致服务不可用,当然,单节点也谈不上分布式。
-
主从复制(Master-Slave Replication) :
- 在主从复制模式下,Redis 可以配置一个主节点和多个从节点。
- 主节点处理写请求,并将数据复制到从节点,从节点处理读请求。
- 这种模式下,Redis 更倾向于 AP(可用性和分区容忍性),因为在网络分区或主节点故障时,从节点仍然可以提供读服务,但可能会导致数据不一致。
-
哨兵模式(Sentinel) :
- 哨兵模式用于监控 Redis 主从复制集群,并在主节点故障时自动进行故障转移(Failover)。
- 哨兵模式提高了系统的可用性,但在故障转移期间可能会有短暂的服务中断。
- 哨兵模式下,Redis 仍然更倾向于 AP,因为在网络分区或故障转移期间,可能会有数据不一致的情况。
-
集群模式(Cluster) :
- Redis集群模式通过分片(Sharding)将数据分布到多个节点上,每个节点负责一部分数据。
- 集群模式下,Redis 可以在一定程度上保证分区容忍性和可用性,但在网络分区或节点故障时,可能会有数据不一致的情况。
- 集群模式下,Redis 更倾向于 AP,因为在网络分区或节点故障时,集群仍然可以提供部分服务,但可能会导致数据不一致。
Redis的配置选项
Redis 提供了一些配置选项,可以在一致性和可用性之间进行权衡。例如:
- replica-read-only:配置从节点是否只读。如果设置为 yes,从节点只处理读请求,不处理写请求,可以提高一致性。
- min-replicas-to-write和min-replicas-max-lag:配置写请求需要等待多少个从节点确认,以及从节点的最大延迟。这些选项可以提高一致性,但可能会降低可用性。
Raft 协议
Raft 协议是一种用于分布式系统中的共识算法,旨在使多个节点在一个集群中就某个值达成一致。
和很多国家领导人大选类似,以投票的方式选举 Leader,票数多则胜出。
而最终获胜的 Leader 则开始做 Leader 节点的事情,比如 接受请求、同步数据给 Follower 或者 执行故障转移等。
Raft协议主要包括以下几个部分:
-
领导选举(Leader Election) :
- 集群中的节点可以处于三种状态之一:领导者(Leader)、候选者(Candidate)和跟随者(Follower)。
- 在正常情况下,集群中只有一个领导者,其他节点都是跟随者。
- 当跟随者在一定时间内没有收到领导者的心跳(Heartbeat)消息时,会发起领导选举,成为候选者并请求其他节点投票。
- 候选者获得多数节点的投票后成为新的领导者。
-
日志复制(Log Replication) :
- 领导者负责接收客户端的请求,并将请求作为日志条目追加到自己的日志中。
- 领导者将日志条目复制到其他跟随者节点,确保所有节点的日志保持一致。
- 当日志条目在多数节点上被复制后,领导者会将该条目提交,并通知跟随者提交该条目。
- Raft 的日志复制是同步的,通过多数确认机制确保日志条目的提交和应用,保证了系统的一致性。
-
安全性(Safety) :
- Raft协议保证在任何给定时间,最多只有一个领导者,并且领导者的日志是最新的。
- 通过选举超时和日志匹配机制,Raft协议确保日志的一致性和系统的安全性。
-
日志压缩(Log Compaction) :
- 为了防止日志无限增长,Raft协议支持日志压缩机制,如快照(Snapshot),以减少存储和提高效率。
Raft 协议的核心思想是通过简化和分解复杂的共识问题,使其更容易理解和实现。以下是一个简单的 Raft 协议状态流转过程:
+-----------+
| Follower |
+-----------+
|
| Election Timeout, Start Election
v
+-----------+
| Candidate |
+-----------+
|
| Receive Votes from Majority
v
+-----------+
| Leader |
+-----------+
|
| Heartbeat Timeout
v
+-----------+
| Follower |
+-----------+
Raft 通过领导选举、日志复制和安全性保证三个主要部分来实现一致性。
Leader 选举
Raft 协议中的领导者选举过程是确保集群中只有一个领导者的关键机制。
1. 初始状态
- 跟随者(Follower) :所有节点初始状态都是跟随者。
- 选举超时(Election Timeout) :每个跟随者都有一个随机的选举超时计时器。
2. 选举超时
- 如果一个跟随者在选举超时内没有收到领导者的心跳(Heartbeat)消息,它会认为当前没有有效的领导者,并开始选举过程。
3. 转变为候选者(Candidate)
- 跟随者将自己的状态转换为候选者。
- 增加当前的任期号(Term)。
- 为自己投票。
- 向其他所有节点发送请求投票(RequestVote)消息。
4. 收集选票
-
其他节点收到请求投票消息后,会根据以下条件决定是否投票:
- 如果请求的任期号大于当前节点的任期号,节点会更新自己的任期号并投票给候选者。
- 如果节点已经投票给其他候选者或当前任期号较大,则拒绝投票。
-
候选者需要获得集群中多数节点的投票才能成为领导者。
5. 成为领导者(Leader)
- 如果候选者获得多数节点的投票,它将成为新的领导者。
- 领导者开始发送心跳消息(AppendEntries)给其他节点,以表明自己的领导地位并保持跟随者的状态。
6. 处理冲突
- 如果在选举过程中,候选者收到来自其他节点的有效领导者的心跳消息,它会放弃选举并重新成为跟随者。
- 如果多个候选者同时发起选举,可能会出现选票分裂的情况。在这种情况下,没有候选者能获得多数选票,所有候选者的选举超时会重新开始,直到某个候选者获得多数选票。
日志复制
日志复制是分布式系统中确保数据一致性和高可用性的重要机制。
它的主要目标是将主节点(Leader)上的数据变更同步到所有从节点(Follower)上,以确保所有节点的数据状态一致:
- Client 发送过来的数据均先到达 Leader 节点,Leader 接收到数据后,先将数据标 记为 uncommitted 状态
- Leader 向所有 Follower 复制数据并等待响应
- Leader在获得集群中⼤于 N/2 个 Follower 的响应后,将数据的状态标记为 committed
- Leader 向 client 发送数据已接收确认,在向 client 发送出已数据接收后,再向所有 Follower 节点发送通知表明该数据状态为 committed
Redis 中 Raft 协议应用
Redis 的 Raft 实现与传统的 Raft 协议有所不同,特别是在日志复制方面。Redis Raft 的设计目标是简化实现,并更适合 Redis 的使用场景。
Redis 哨兵模式
Redis 哨兵模式主要用于监控 Redis 主从复制集群,并在主节点故障时自动进行故障转移。它的主要目标是提高系统的高可用性。
哨兵模式的特点
-
监控和通知:
- 哨兵节点定期检查主节点和从节点的健康状态,并在检测到故障时通知其他哨兵节点和客户端。
-
故障转移:
- 哨兵节点通过选举机制选出一个新的主节点,并将原来的从节点重新配置为新的主节点的从节点。
-
配置提供:
- 哨兵节点向客户端提供当前主节点的地址,确保客户端能够连接到正确的主节点。
哨兵模式与 Raft 的不同
- 一致性:哨兵模式更倾向于 AP(可用性和分区容忍性),在网络分区或主节点故障时,从节点仍然可以提供读服务,但可能会导致数据不一致。
- 故障转移:哨兵模式的故障转移是通过哨兵节点的选举机制实现的,而不是通过日志复制和多数确认机制。
- 日志复制:哨兵模式下的日志复制是异步的,主节点将数据变更异步复制到从节点,这意味着在主节点故障时,可能会有少量数据丢失。
Redis 集群模式
Redis 集群模式通过分片(Sharding)将数据分布到多个节点上,每个节点负责一部分数据。集群模式的主要目标是实现数据分片和分布式一致性。
集群模式的特点
-
数据分片:
- Redis 集群将数据分成 16384 个哈希槽(Hash Slot),每个节点负责一部分哈希槽。
-
请求路由:
- 客户端通过集群节点的元数据(Metadata)了解每个哈希槽对应的节点,并将请求路由到正确的节点。
-
故障转移:
- 集群模式下,每个主节点都有一个或多个从节点。当主节点故障时,从节点会自动提升为主节点。
-
一致性保证:
- Redis 集群通过异步复制实现数据一致性,主节点将数据变更异步复制到从节点。
集群模式与 Raft 的不同
- 一致性:集群模式更倾向于 AP(可用性和分区容忍性),在网络分区或节点故障时,集群仍然可以提供部分服务,但可能会导致数据不一致。
- 数据分片:集群模式通过数据分片实现扩展性,而 Raft 通常用于单个日志的复制和一致性。
- 日志复制:集群模式下的日志复制是异步的,而 Raft 的日志复制是同步的,通过多数确认机制确保一致性。
总结
- Redis 哨兵模式:主要用于高可用性和故障转移,更倾向于 AP,在网络分区或主节点故障时,可能会有数据不一致。
- Redis 集群模式:通过数据分片和异步复制实现分布式一致性,更倾向于 AP,在网络分区或节点故障时,可能会有数据不一致。
- Raft 协议:一种 CP 协议,通过领导选举和日志复制实现一致性,确保在网络分区或节点故障时,系统仍然能够达成一致。
系统设计者可以根据具体的需求和场景,选择合适的模式和协议,以实现最佳的性能和可靠性。