🌈🌈🌈🌈🌈🌈🌈🌈
欢迎关注公众号(通过文章导读关注:【11来了】),及时收到 AI 前沿项目工具及新技术
的推送
发送 资料
可领取 深入理解 Redis 系列文章结合电商场景讲解 Redis 使用场景
、中间件系列笔记
和编程高频电子书
!
文章导读地址:点击查看文章导读!
🍁🍁🍁🍁🍁🍁🍁🍁
ZooKeeper 中的分布式一致性协议 ZAB
zk 使用了 ZAB( ZooKeeper Atomic Broadcast) 分布式一致性协议来保证在分布式系统中的所有节点可以 保证数据一致性
下边将从具体的功能出发,来介绍 ZAB 协议的原理
!
ZAB 协议如何实现主从同步机制?
在 zk 集群中,只有 Leader 可以接收写操作,Follower 只可以读,Leader 收到写的事务请求后,会香所有的 Follower 发送一个 事务操作的提议
,也就是 Proposal
,当 Follower 收到 Proposal 之后,会先将数据的变更写入到磁盘的日志文件中,表示已经收到了 Proposal,之后会返回 Ack 给 Leader,当 Leader 收到了超过半数 Follower 的 Ack,之后 Leader 会先将数据写到自己的 znode 中(也就是写到内存中去,此时数据就可以被客户端感知到了),之后再给所有的 Follower 发一个 Commit 消息,让大家提交这个请求事务,Follower 收到 Commit 消息后,就会将磁盘中刚刚写入的数据往内存中的 znode 中写,之后客户端就可以读取到数据了
光读上边的字,可能看起来很头疼,可以通过下边这个图很清晰的了解整个流程:
ZAB 协议如何实现崩溃恢复机制?
下边将会介绍 zk 集群 启动
再到 崩溃
再到 恢复
整体的流程:
zk 集启动的时候,进入 恢复模式
,选举一个 Leader 出来,然后 Leader 等待集群中过半的 Follower 跟他进行数据同步,只要过半的 Follower 完成数据同步,接着就退出恢复模式,可以对外提供服务了
此时,还没完成同步的 Follower 会自己去跟 Leader 进行数据同步的
之后会进入 消息广播模式
,只有 Leader 可以接受写请求,但是客户端可以任意连接 Leader 或者 Follower,如果客户端连接到 Follower,Follower 就会将写请求转发给 Leader
Leader 收到写请求,就把请求同步给所有的 Follower,当超过半数的 Follower 都返回了 Ack,之后 Leader 先将数据写到自己的 znode 中,再给所有的 Follower 发一个 Commit 消息,让大家提交这个请求事务,Follower 收到 Commit 消息后,就会将磁盘中刚刚写入的数据往内存中的 znode 中写,之后客户端就可以读取到数据了
如果 Leader 宕机了,就会进入 恢复模式
,重新选举一个 Leader,只要获得了过半的机器的投票,就可以成为 Leader
zk 集群中可以容忍不超过一半的机器宕机,就比如说一个集群有 3 台机器,那么最多允许 1 台机器宕机,剩下的 2 台选举 Leader,只要 2 台机器都认可其中一台机器当 Leader,也就是超过了集群一半的机器都认可,那么就可以选举这台机器作为 Leader
新的 Leader 等待过半的 Follower 跟他同步,之后重新进入 消息广播模式
以上就是 zk 集群恢复崩溃的整个流程了,当然我也花了一个流程图,更方便观看,如下:
主要就是分为 3 个阶段:
- 集群启动时:恢复模式,Leader 选举 + 数据同步
- 消息写入时:消息广播模式,Leader 采用 2PC 的过半写机制,给 Follower 进行同步
- 崩溃恢复:恢复模式,Leader/Follower 宕机,只要剩余机器超过一半,就可以选举新的 Leader
下边来介绍一下 ZAB 协议中是如何采用 2PC 两阶段提交思想完成数据写入的:
采用 2PC 两阶段提交思想
的 ZAB 消息广播流程:
每一个消息广播的时候,都是基于 2PC 的思想,先是发起事务提议 Proposal 的广播,各个 Follower 返回 Ack,当过半的 Follower 都返回 Ack 之后,Leader 就发送 Commit 消息到 Follower,让大家提交事务
这里的两阶段指的就是发送 Proposal
和 Commit
!
发起一个事务 Proposal 之前,Leader 会分配一个全局唯一递增的事务 id(zxid),以此来严格保证顺序
Leader 会为每个 Follower 创建一个队列,里边存放要发给 Follower 的事务 Proposal,保证了一个同步的顺序性
Follower 收到事务 Proposal 之后,就立即写入本地磁盘日志中,写入成功后数据就不会丢失了,之后返回 Ack 给 Leader,当过半的 Follower 都返回 Ack,Leader 推送 Commit 消息给全部 Follower,让大家进行事务提交
那么 zk 到底是 强一致性
还是 最终一致性
?
zk 不是强一致的
,因为当 Leader 给 Follower 发送 Commit 消息之后,可能有的 Follower 提交成功了,有的还没有提交成功,这会导致 短暂的数据不一致
但是说 zk 是最终一致性也不太对,zk 官方给自己的定位是 顺序一致性
,因为 Leader 会保证所有的事务 Proposal 同步到 Follower 上都是按照顺序来执行的
ZAB 协议下可能存在的 数据一致性问题
:
在 ZAB 写一下有两种可能造成数据不一致的情况
-
第一种情况
:Leader 在收到过半 Follower 的 Ack 之后,Leader 就会 Commit,如果 Leader 在自己 Commit 之后,还没来得及给 Follower 发送 Commit 就挂掉了,此时 Leader 和所有的 Follower 的数据都是不一致的所以在 Leader 崩溃的时候,就会选举一个拥有最大
事务 id
的机器作为 Leader,它需要去检查事务日志,如果发现自己磁盘日志里有一个 Proposal 并且没有提交,说明肯定是之前的 Leader 没来得及发送 Commit 就挂掉了,此时新选举的 Leader 就为这个 Proposal 发送 Commit 到其他所有的 Follower 中去,这样就保证了老 Leader 提交的事务最终可以同步到所有的 Follower 中去
-
第二种情况
:Leader 收到客户端请求,结果还没来得及给 Follower 发送 Proposal 就挂了,此时这个 Leader 上的 Proposal 请求应该是要被丢弃的,这种情况下,当新的 Leader 选举出来之后,老的 Leader 作为 Follower 重新启动,看到自己的磁盘日志有一个事务 Proposal,并且发现这个 Proposal 其实不应该存在,那么直接丢弃就可以了
那么在 第二种情况
中,需要丢弃的消息是如何在 ZAB 协议中进行处理的?
每一条事务的 zxid 都是 64 位的,高 32 位是 Leader 的 epoch,可以看作是 Leader 的版本,低 32 位才是自增长的事务 id
如果 Leader 没有来得及给 Follower 发送 Proposal 就挂掉了,那么新的 Leader 选举出来之后,它的 epoch 会增长 1,老的 Leader 成为 Follower 之后,发现自己比新的 Leader 多一条 Proposal,并且 Proposal 的 epoch 比新 Leader 的 epoch 要小,那么直接丢弃即可