知乎高赞: 为什么同样是分布式架构的 Kafka 需要 Leader 而 Redis 不需要
Redis 不需要 Leader 这个观点其实有歧义,是不准确的,这个问题本质其实是涉及数据分片、数据副本一致性,接下来将为大家一一解答。
1、Redis Cluster 架构
在 Redis3.0 版本开始,Redis 引入了一种去中心化的集群架构,采用预分片的模式,一个集群中所有节点总共对应 16384 个槽位,在对一个 key 进行写入时,首先对 key 取 hashcode,然后求模来映射到具体的某一个节点,其部署架构如下图所示:
上述每一个节点中存储的数据都不一样,即每一个节点存储整体数据的一部分,并且为了实现去中心化每一个节点需要存储集群中所有 key 所对应存档的节点信息(即 Key 的路由信息),这样当客户端将查询 key1 的请求发送到 redisA 节点,但该 key1 实际存储在 redisB 节点,此时 A 节点需将该节点路由到实际存储该 key 的节点,内部实现一个重定向,从而实现访问任意一个节点都能查询到存储的值。
在上述架构中是不需要存在 Leader 的,这也是所谓的集群去中心化设计思想的关键,但问题来了,如果集群中任意一个节点宕机不可用,存储在该节点中的数据就会丢失,为了解决这个问题,通常会引入主从架构,架构图如下所示:
具体的做法是为每一个主节点引入一个或多个从节点,用来拷贝主节点的数据,上图中的每一个虚线框表示一个复制组,也称之为副本,副本之间的数据期望完全一致。
在主从架构中如何保证数据一致性呢?通常主从集群与客户端之间的交互方式有如下几种:
客户端发送写请求到 Master,在 Master 节点写入成功就返回给客户端,同时从节点异步复制数据,主从存在延迟,并且当主节点宕机存在丢数据的风险。
客户端发送写请求到 Master,Master 节点写入成功后,需要等待从从节点同样写入成功后才会向客户端返回成功,该方式会增大延迟,增加主从数据延迟,但还是无法避免主从数据不一致。
上述两种情况,都无法确保数据在主从两个节点上的一致性。
为什么同步双写也无法保证数据的一致性呢?
客户端只有在 master,slave 同步写入成功后才会收到响应,乍一看,能提供一致性,其实不然,试想一下,例如将 key1 的数据先写入到 Master 节点,在写入从节点的过程中出现错误,客户端会收到写入失败,但此时去 master 中查询 key1 的数据,却能查询出上一次请求失败的数据,即客户端虽然收到了写入失败,但主节点却写入成功,造成了数据语义上的不一致性。
即主从同步这种架构,主从节点、客户端的确认机制存在天然的不足,为了解决该问题,Raft 等分布式副本数据强一致性协议就闪亮登场了。
2、副本之间强一致性协议
为了解决数据的高可用性,避免单点故障,通常会将数据同步为多份,高可用性是解决了,但带来了另外一个问题,多个副本数据之间如何保证一致性,为了解决该问题出现了诸如 raft、paxos 等一致性协议。
Raft 协议的数据复制说明图如下:
图中客户端向 Raft 协议集群发起一个写请求,集群中的 Leader 节点来处理写请求,首先数据先存入 Leader 节点,然后需要广播给它的所有从节点,从节点接收到 Leader 节点的数据推送对数据进行存储,然后向主节点汇报存储的结果,Leader 节点会对该日志的存储结果进行仲裁,如果超过集群数量的一半都成功存储了该数据,主节点则向客户端返回写入成功,否则向客户端写入写入失败。
如果只有主节点写入成功,但其他从节点没有写入成功,就算数据被写入到 Leader 节点,但这部分数据对客户端来说是不可见的。
Raft 协议主要分为两个部分:Leader 节点选举与日志复制。
Leader 节点选举:从集群中选举一个 Leader 节点用于处理数据的读写,从节点只负责从 Leader 节点同步数据,并且 Leader 节点宕机,会自动触发选举,选举出一个新的 Leader 节点。
日志复制:数据写入主节点后,主节点需要将数据转发给从节点,只有集群中超过半数节点都成功将一条数据写入才向客户端返回成功。
Raft 协议的实现细节本文不打算深究,大家如果感兴趣,可以在文末获取笔者的电子书,其中有一个系列专门阐述 Raft 协议的实现原理,本文只从设计层面剖析为什么 Raft 协议能实现数据的一致性。
笔者认为 Raft 协议能确保数据的一致性,主要是引入了全局日志序号与已提交指针。
2.1 引入了全局日志序号
为了方便对日志进行管理与辨别,raft 协议为一条一条的消息进行编号,每一条消息达到主节点时会生成一个全局唯一的递增号,这样可以根据日志序号来快速的判断数据在主从复制过程中数据是否一致。
2.2 已提交指针
我们知道,日志先写入主节点,然后再进行传播,在集群中超过半数节点的写入成功之前,这条日志都不能认为写入成功,尽管已经存储到了主节点中,为了让客户端对这条日志不可见,Raft 协议引入了已提交指针,只有小于等于已提交的数据才能被客户端感知。
一条日志要能被提交的充分必要条件是日志得到了集群内超过半数节点成功追加,才能被认为已提交,才会向客户端返回成功,这样就实现了数据在集群内、客户端与集群之间的数据一致性语义。
为了让大家更加深入的理解 Raft 协议数据性一致性问题,给出如下思考题,主从切换会导致 Raft 丢失数据吗?
例如一个 Raft 协议中有 3 个节点,各个节点的写入情况如下:
Node1:100
Node2:89
Node3:88
其中 Node1 为 Leader 节点,如果 Node1 节点宕机,整个集群触发重新选举,会丢失数据吗?
答案是肯定不会的。
首先我们要先明白,在上面的状态下,已提交指针为 89,因为集群有两个节点都成功写入了 89,即向客户端返回成功的数据也是序号为 89 的数据,在选举过程中,Node3 不可能会被选举为 Leader,因为 Node3 中存储的数据小于 Node2 存储的数据,当 Node2 选举为新的 Leader 时,Node3 会向 Node2 同步数据。
3、总结
本文从知乎上一个不严紧的问题出发,挖掘该问题的本质:分布式数据存储的数据分片与高可用(避免单点故障),从而又引发新的问题(数据副本之间的一致性)
本期就介绍到这里了,希望对你有所帮助,同时也希望一键三连,给作者一些鼓励。
见字如面,我是威哥,一个从普通二本院校毕业,从未曾接触分布式、微服务、高并发到通过技术分享实现职场蜕变,成长为 RocketMQ 社区优秀布道师、大厂资深架构师,出版《RocketMQ 技术内幕》一书,欢迎大家关我,可加我微信 dingwpmz,一起交流进步。
分享笔硬核的电子书,您将获取日均千亿级消息流转的 MQ 集群运维能力:
获取方式:微信搜索【中间件兴趣圈】,回复 RMQPDF 即可获取。
版权声明: 本文为 InfoQ 作者【中间件兴趣圈】的原创文章。
原文链接:【http://xie.infoq.cn/article/f6961a239afc8fda712c9cbc5】。未经作者许可,禁止转载。
评论