面试题:什么是 CAP 定理
要求
Consistency
一致性:访问分布式系统中任意节点,总能返回一致的结果 Availability
可用性:分布式系统总能向客户端返回响应 Partition tolerance
分区容忍:当分布式系统(内部)节点间通信发生了消息丢失或消息延迟,仍然允许系统继续运行 CAP 定理:最多三选二,无法兼得,通常在 CP 或者 AP 之间做出选择
client 向 Node1 写入新值 v1
写入成功 Node1 更新成 v1
Node1 在没有将变更同步到 Node2 时,就向客户端返回了应答
client 发起向 Node2 的读操作
返回了旧值 v0(不一致)的结果
client 向 Node1 写入新值 v1
写入成功 Node1 更新成 v1,此时不能立刻向 client 返回应答,而是需要将 v1 同步到 Node2
同步 v1 成功
此时才能向 client 返回应答
如果此时 client 再去访问 Node2,不会出现不一致的情况
当发生了网络分区,Node1 与 Node2 已经失去了联系,这时仍想对外提供服务(保 P)
client 向 Node1 写入新值 v1
写入 Node1 成功,但无法同步至 Node2
这时为了保证一致性,Node1 不能向 client 返回应答,造成操作挂起无法完成(失去可用性)
当发生了网络分区,Node1 与 Node2 已经失去了联系,这时仍想对外提供服务(保 P)
client 向 Node1 写入新值 v1
写入 Node1 成功,但无法同步至 Node2
为了保证可用性,向 client 返回了应答(但牺牲了一致性)
CP 和 AP 之间需要做权衡,其实根据需求不同,也可以将一致性划分成几个级别,在这些级别里做一个权衡。
Paxos
、Raft
、ZABGossip
Paxos - 如何保证强一致性
要求
问题提出
集群中有 N 个节点,如果一个节点写入后要求同步到剩余 N-1 个节点后再向客户端返回 ok,虽然看起来最保险,但其中任意一个节点同步失败,势必造成整个集群不可用,能否在此基础上稍微提高可用性呢?
答案是 (写)多数派,集群节点设置为奇数,同步超过集群中 N/2 个节点成功,则向客户端返回 ok,但存在顺序性问题,如 3 描述
多数派写操作成功后的读一致性暂不考虑,思考下图中的两项操作,都满足了多数派通过,但 S3 这台服务器并没有与 S1,S2 达成一致,要达到多数派内部一致性【多个写操作之间存在顺序性问题】
Paxos 是一种共识算法,目的是解决之前提到的写多数派时的顺序性问题
Paxos 角色划分:集群中的每个节点都可以充当
Proposer:负责生成提案
Acceptor:负责批准提案
Learner:负责获取提案,Acceptor 批准提案后,会将提案发送给所有 Learner
执行一个修改操作,不是一上来就能执行,分成两个阶段:
算法要点:
提案号
,接受阶段发送提案号 + 值
提案号
n 唯一且全局递增,大的提案号
有更高优先级已接受值
,就会替换掉 Proposer 自己原来的值,保证一致性acceptValue(已接受值)的优先级要比value(新传进来的)要高!
P 广播提案号 1
有 3 个 A 接到提案,此时满足 n > minN,将 minN 更新为 1
3个 A 成功返回,P 收到的应答过半,但没有遇到更大的 acceptNo 和 acceptValue,因此使用自己的 value X
P 广播提案号和值 1:X
3 个 A 接到提案号和值,更新状态,返回 minN 值 1 给 P
P 收到过半应答,并检查发现没有出现 minN > 1,便选中提案值 X
产生生了冲突也能保持一致性
acceptValue(已接受值)的优先级要比value(新传进来的)要高!
S1 广播提案号 1,想把值更新为 X
S5 广播提案号 2,想把值更新为 Y
S1、S2、S3 已经经历了 Accept 阶段并选中值 X
关键点,S3 也接到了 S5 的prepare 提案,这时是否会有不一致的情况呢?
此时 S3 状态已将 acceptN 和 acceptValue 分别更新为 1:X;再返回 S5 的 ack 时就会将 1:X 返回给 S5
S5 用返回的 X 替换掉了自己原有的值 Y,并执行后续流程,后续都会同步为 X
S1 广播提案号 1,想把值更新为 X
S5 广播提案号 2,想把值更新为 Y
S1、S2、S3 已经经历了 Accept 阶段,与例2 不同的是,值 X 还未选中
关键点,S3 也接到了 S5 的prepare 提案,这时是否会有不一致的情况呢?
此时 S3 状态将 acceptN 和 acceptValue 分别更新为 1:X;再返回 S5 的 ack 时就会将 1:X 返回给 S5
S5 用返回的 X 替换掉了自己原有的值 Y,并执行后续流程,后续都会同步为 X
S1 广播提案号 1,想把值更新为 X
S5 广播提案号 2,想把值更新为 Y
关键点,S3 还未经历 Accept 阶段时,就拿到了 S5 的 prepare 提案,这时是否会有不一致的情况呢?
S3 在接到 S1 的 accept 请求时,n>=minN 条件不成立,因此没有更新 acceptN 和 acceptValue,并且返回的 minN 是 2
对 S1 来说,S3 返回的 minN 大于 n,选中失败;想更新 X 需要发起新一轮提案
对 S5 来说,accept 阶段发送的是它自己的 2:Y,后续会把值同步为 Y
两个服务器都相对同一个变量进行修改,能保证一致性吗:不能;
但是借用了 Paxos
算法就可以啦;
回顾最早提到的顺序性问题,看 Paxos 能否解决它
下图演示了 Paxos 是如何解决顺序性问题的,分析步骤参考例3
参考资料
- https://www.youtube.com/watch?v=JEpsBg0AO6o&t=41s Raft 作者讲解 Paxos
要求
Raft 算法
另一种共识算法,目的是比 Paxos 更易理解
整个 Raft 算法分解为三部分:
Leader
选举
① 只有一个 Server 能作为 Leader
② 一旦此 Server 崩溃,选举新 Leader
执行操作,(以日志复制为例Log replication)
① 由 Leader 执行自己的日志记录
② 将日志复制到其它 Server,会覆盖掉不一致的部分
③ 多数派记录日志成功,Leader 才能执行命令,向客户端返回结果
确保安全
① 保证日志记录的一致性
② 拥有最新日志的 Server 才能成为 Leader
Leader 会不断向选民发送 AppendEntries 请求,证明自己活着
选民收到 Leader AppendEntries 请求后会重置自己的 timeout 时间
选民收不到 AppendEntries 请求超时后,转换角色为候选者,并将任期加1,发送 RequestVote 请求,推选自己
选民收到第一个 RequestVote,会向该候选者投一票,这样即使有多个候选者,必定会选出一个 Leader,选票过半即当选,如果落选会变回选民
每一任期最多有一个 Leader,但也可能没有(选票都不过半的情况,需要再进行一轮投票,timeout 在 T~2T 间随机)(T是两个节点之间单向的传输时间)
任期由各个 server 自己维护即可,无需全局维护,在超时后加1,在接收到任意消息时更新为更新的任期,遇到更旧的任期,视为错误
客户端发送命令至 Leader
Leader 将命令写入日志(S1虚框),并向所有选民发送 AppendEntries 请求
多数派通过后,执行命令(即提交,S1虚框变实),此时就可以向客户端返回结果
在后续的 AppendEntries 请求中通知选民,选民执行命令(即提交,S2,S3,S4,S5虚框变实)
如果选民挂了,则 Leader 会不断尝试,待到选民重启,会将其缺失的日志陆续补齐
Leader 日志的完整性
随 RequestVote 请求发送,如果候选者的日志还没选民的新,则投否决票选民日志的一致性
以 Leader 为准,对选民的日志进行补充或覆盖
AppendEntries 请求发送时会携带最新的
以及上一个的
如果选民发现上一个的
能够对应上则成功,否则失败,继续携带更早的信息进行比对
图中 Leader 发送了 <3,4,Command>
和 <2,3>
给 follower,follower 发现 <2,3>
能够与当前最新日志对应,这时直接执行 <3,4,Command>
即可
图中 Leader 发送了 <3,4,Command>
和 <2,3>
给 follower,follower 发现 <2,3>
不能与当前最新日志对应,会央求 Leader 发送更早日志
Leader 这次发送了 <3,4,Command>
, <2,3,Command>
,<1,2>
给 follower,follower 发现 <1,2>
能够与当前最新日志对应,这时补全 <3,4,Command>
, <2,3,Command>
即可
参考资料
- https://www.youtube.com/watch?v=vYp4LYbnnW8 Raft 作者讲解 Raft
- https://raft.github.io/ Raft 资源
- https://raft.github.io/raftscope/index.html Raft 可视化
要求
Gossip 协议
与 Paxos
和 Raft
目标是强一致性不同,Gossip
达到的是最终一致性
它可以快速地将信息散播给集群中每个成员,散播速度为 𝑙𝑜𝑔𝑓(𝑁)𝑙𝑜𝑔_𝑓 (𝑁)logf(N) ,其中 f
术语称为 fanout,代表每次随机传播的成员数,而 N
代表总共成员数。例如:
Gossip 协议工作流程
Gossip 协议优点
参考资料
- https://flopezluis.github.io/gossip-simulator/# Gossip 可视化
要求
提示
- 这里介绍以思想为主,因为实现和涉及的框架实在是太多了
答案:通过心跳
建议阅读《大型网站技术架构 – 核心原理与案例分析》一书,李智慧著,优点是条理清晰,不像另一些东拼西凑的文章
节录、概要如下:
应用层高可用
服务层高可用
数据层高可用
需要有数据备份机制与故障转移机制
缓存服务是否需要高可用,两种观点:
① 缓存服务不可用会让数据库失去保护,因此需要保证缓存服务高可用
② 缓存服务不是数据存储服务,缓存宕机应当通过其他手段解决,如扩大缓存规模,一个缓存服务器的宕机只会影响局部
负载均衡:即使用多台服务器共同分担计算任务,把网络请求和计算按某种算法均摊到各个服务器上
参考文档
- https://nginx.org/en/docs/http/load_balancing.html
- https://dubbo.apache.org/zh/docsv2.7/user/references/xml/dubbo-provider/
所谓分片就是指数据量较大时,对数据进行水平切分,让数据分布在多个节点上。(目的1:解决单个节点的运算存储上限问题;目的2:采用冗余的策略,让数据有多个副本提高可用性)
存在问题
要点
要点
2PC 和 TCC 都属于同步方案,实际开发中更多采用的是异步方案
问题转换成保证本地事务与消息投递的原子性
例如:RocketMQ 的解决方案如下
① 发送消息到 broker ,只是此时消息称为半消息,无法消费
② 执行本地事务,如果成功,则半消息转换为正式消息,允许被消费;如果失败,删除 broker 上的半消息
③ 对于 broker 这端,如果迟迟不能收到半消息的 commit 或 rollback 信息,则会回查本地事务是否完成,根据状态确定如何处理
前面讲负载均衡和数据分片时,都提到了一致性 Hash,它是为了解决在服务器增、删时普通 hash 算法造成数据大量迁移问题的
普通 hash 算法
一致性 hash 算法
假设有 3 台服务器,10 个 key 在服务器上的分布如下图所示
添加一台服务器后,数据分布变成下图,发现仅有 3 个key 需要迁移(上下颜色不同的)