Redis集群原理

Redis集群->拓扑结构

Redis 集群是一个网状结构,无中心结构,每个节点都通过 TCP 连接跟其他每个节点连接。
在一个有 N 个节点的集群中,每个节点都有 N-1 个流出的 TCP 连接,和 N-1 个流入的连接。 这些 TCP 连接会永久保持,并不是按需创建的。
节点们使用一个 gossip 协议来传播集群的信息,这样可以:发现新的节点、 发送ping包(用来确保所有节点都在正常工作中)、在特定情况发生时发送集群消息。集群连接也用于在集群中发布或订阅消息。

 

 

Redis集群->键分布模型

键空间被分割为 16384 槽(slot),事实上集群的最大节点数量是 16384 个。(然而建议最大节点数量设置在1000这个数量级上)
Hash Key如下:
HASH_SLOT = CRC16(key) mod 16384
举个例子:
有三个Redis节点,节点1、2、3负责这些Slot范围:0 - 5461,5462 - 10924,10924 - 16384

 

 

关于SLot的如何分配的?
官方提供了redis-trib的Ruby程序,其实就是集群管理工具,这个程序通过向实例发送特殊命令来完成创建新集群, 检查集群, 或者对集群进行重新分片(reshared)等工作。Reshard是将Redis节点负现的Slot进行重分配,运维只需要发出Reshard指令,Reshard的过程是Redis集群自动进行的

Redis集群->客户端实现

Moved重定向

一个 Redis 客户端可以自由地向集群中的任意节点(包括从节点)发送查询。接收的节点会分析查询,如果这个命令是集群可以执行的(就是查询中只涉及一个键),那么节点会找这个键所属的哈希槽对应的节点。
如果刚好这个节点就是对应这个哈希槽,那么这个查询就直接被节点处理掉。否则这个节点会查看它内部的 哈希槽 -> 节点ID 映射,然后给客户端返回一个 MOVED 错误。
这个错误包括键(3999)的哈希槽和能处理这个查询的节点的 ip:端口号(127.0.0.1:6381)。客户端需要重新发送查询到给定 ip 地址和端口号的节点。 注意,即使客户端在重发查询之前等待了很长一段时间,与此同时集群的配置信息发生改变,如果哈希槽 3999 现在是为其他节点服务,那么目标节点会再向客户端回复一个 MOVED 错误。
当集群是稳定的时候,所有客户端最终都会得到一份哈希槽 -> 节点的映射表,这样能使得集群效率非常高:客户端直接定位目标节点,不用重定向、或代理或发生其他单点故障

Ask重定向

为什么我们不能单纯地使用 MOVED 重定向呢?因为当我们使用 MOVED 的时候,意味着我们认为哈希槽永久地被另一个不同的节点处理,并且希望接下来的所有查询都尝试发到这个指定的节点上去。而 ASK 意味着我们只要下一个查询发送到指定节点上去。

 

 

 

上图中,配置Slot1需要从节点A迁移到节点B,迁移过程中Key1、Key2已迁到节点B,而Key3、Key4仍在节点A。客户端之前保存的映射表中Slot1 -> 节点A,所以客户端来节点A来查询Key1、Key2时,节点A已不负责这两个Key,因而返回Ask重定向,通知客户端去节点B去获取数据。由于是Ask重定向,客户并不会更新Slot1 -> 节点A的映射表(除非迁移过程完成),这样在获取Key3、Key4的数据时,客户端可以一次性获取到。

Redis集群->一致性保证

Redis 并不能保证数据的强一致性. 这意味这在实际中集群在特定的条件下可能会丢失写操作.

  1. 原因是因为集群是用了异步复制. 写操作过程:
    客户端向主节点B写入一条命令.
    主节点B向客户端回复命令状态.
    主节点将写操作复制给他得从节点 B1, B2 和 B3.
    主节点对命令的复制工作发生在返回命令回复之后, 因为如果每次处理命令请求都需要等待复制操作完成的话, 那么主节点处理命令请求的速度将极大地降低 —— 我们必须在性能和一致性之间做出权衡。 注意:Redis 集群可能会在将来提供同步写的方法。
  2. Redis 集群另外一种可能会丢失命令的情况是集群出现了网络分区, 并且一个客户端与至少包括一个主节点在内的少数实例被孤立。


     

Redis集群->同步时钟

Redis集群->集群阶段,逻辑时钟

本质上说,epoch 是一个集群里的逻辑时钟,并决定一个给定的消息赢了另一个带着更小 epoch 的消息。
,它是用来记录事件的版本号,所以当有多个节点提供了冲突的信息的时候,另外的节点就可以通过这个状态来了解哪个是最新的。 currentEpoch 是一个 64bit 的 unsigned 数。
Redis 集群中的每个节点,包括主节点和从节点,都在创建的时候设置了 currentEpoch 为0。
当节点接收到来自其他节点的 ping 包或 pong 包的时候,如果发送者的 epoch(集群连接消息头部的一部分)大于该节点的 epoch,那么更新发送者的 epoch 为 currentEpoch。
由于这个语义,最终所有节点都会支持集群中较大的 epoch。

Redis集群->配置阶段,配置时钟

每一个主节点总是通过发送 ping 包和 pong 包向别人宣传它的 configEpoch 和一份表示它负责的哈希槽的位图。
当一个新节点被创建的时候,主节点中的 configEpoch 设为零。
从节点由于故障转移事件被提升为主节点时,为了取代它那失效的主节点,会把 configEpoch 设置为它赢得选举的时候的 configEpoch 值。
configEpoch 用于在不同节点提出不同的配置信息的时候(这种情况或许会在分区之后发生)解决冲突。(按我理解:ConfigEpoch是管配置更新的,有需要才去更新configEpoch,而像Epoch是定时包来更新)
从节点也会在 ping 包和 pong 包中向别人宣传它的 configEpoch 域,不过从节点的这个域表示的是上一次跟它的主节点交换数据的时候主节点的 configEpoch 值。这能让其他个体检测出从节点的配置信息是不是需要更新了(主节点不会给一个配置信息过时的从节点投票)。
每次由于一些已知节点的值比自己的值大而更新 configEpoch 值,它都会永久性地存储在 nodes.conf 文件中。
当一个节点重启,它的 configEpoch 值被设为所有已知节点中最大的那个 configEpoch 值。

Redis集群->选举机制

从节点的选举和提升都是由从节点处理的,主节点会投票要提升哪个从节点。一个从节点的选举是在主节点被至少一个具有成为主节点必备条件的从节点标记为 FAIL 的状态的时候发生的。
当以下条件满足时,一个从节点可以发起选举:

  • 该从节点的主节点处于 FAIL 状态。
  • 这个主节点负责的哈希槽数目不为零。
  • 从节点和主节点之间的重复连接(replication link)断线不超过一段给定的时间,这是为了确保从节点的数据是可靠的。


     

    选举完成后,从节点正式提升为主节点,并需要更新其在集群中负责的配置


     

Redis集群 -> 备份迁移

Redis 集群实现了一个叫做备份迁移(replica migration)的概念,以提高系统的可用性。在集群中有主节点-从节点的设定,如果主从节点间的映射关系是固定的,那么久而久之,当发生多个单一节点独立故障的时候,系统可用性会变得很有限。(其实就是一主多备的机制,多备的机器可以复用给其它的主)

 

image.png
 

假设集群有两个主节点 A,B。
1.节点 A 有一个从节点A1 。节点 B 有两个从节点:B1 和 B2。
2.主节点 B 失效。B1 被提升为主节点。
3.节点 B2 迁移成为节点 A1 的从节点,要不然 A1 就没有任何从节点。
4.三个小时后节点 A1 也失效了。
5.节点 B2 被提升为取代 A1 的新主节点。
集群仍然能继续正常工作。选新备机的方法:找备机最多的主要

posted @ 2019-10-10 22:06  dk_tuke  阅读(814)  评论(1编辑  收藏  举报