vlambda博客
学习文章列表

Redis集群为了解决什么问题而存在的?

Redis集群是一个distribute、fault-tolerant的Redis实现,主要设计目标是达到线性可扩展性、可用性、数据一致性。

一、一些特性

线性拓展 官方推荐最大的节点数量为1000,由于Cluster架构中无Proxy层,Master与Slave之间使用异步replication。

数据一致性 客户端容忍一定程度的数据丢失,集群尽可能保存Client write操作的数据,保证数据一致性。

可用性 Redis集群通过partition来提供一定程度的可用性,当集群中的一部分节点失效或者无法进行通讯时,集群仍可以继续提供服务。

  • 只要集群中大多数Master可达、且失效的Master至少有一个Slave可达,即集群非Fail状态,集群都是可用的。

  • Redis集群的replicas migration机制可以将拥有多个Slave的Master的某个Slave,迁移到没有Slave的Master下,即Slave分布相对平衡,确保Master都有一定数量的Slave备份。

集群节点属性 集群中每个Master node负责存储数据、集群状态,包括slots与nodes对应关系。Master nodes能够自动发现其他nodes,检测failure节点,当某个Master节点失效时,集群能将核实的Slave提升为Master。

Keys分布模型 集群的键空间被分割为16384个slots(即hash槽),slot是数据映射的基本单位,即集群的最大节点数量是16384(官方推荐最大节点数量为1000个左右)。集群中的每个Master节点负责处理16384个hash槽其中的一部分,当集群处于“stable”状态时(无slots在节点间迁移),任意一个hash slot只会被单个node所服务。以下是键映射到hash槽的算法:

HASH_SLOT = CRC16(key) mod 16384

数据一致性保证 Redis集群尽可能保证数据的强一致性,但在特定条件下会丢失数据,原因有两点:异步replication机制以及network partition。

Master以及对应的Slaves之间使用异步的机制,在节点failover后,新的Master将会最终替代其他的replicas:

  • write命令提交到Master,Master执行完毕后向Client返回“OK”,但由于一部分replication,此时数据还没传播给Slave;如果此时Master不可达的时间超过阀值,此时集群将触发对应的slave选举为新的Master,此时没有replication同步到slave的数据将丢失。

在network partition时,总有一个窗口期(node timeout)可能会导致数据丢失:

  • 由于网络分区,此时master不可达,且Client与Master处于一个分区,且此时集群处于“OK”。此时Failover机制,将其中一个Slave提升为新的Master,等待网络分区消除后,老的Master再次可达,此时节点被切换为Slave,而在这段期间,处于网络分区期间,Client仍然将write提交到老的Master,因为该Master被认为是仍然有效的。当老的Master再次加入集群,被切换成Slave后,这些数据将永远丢失。

集群可用性 上述谈到多次集群状态的概念,那集群什么时候处于“OK”,什么时候处于“FAIL”,节点什么时候可用等,详见下面的解释: 当NODE_TIMEOUT时,触发failover,此时集群仍然可用的前提是:“大分区”(相对发生网络分区的Client-Master小分区端而言)端必须持有大部份Masters,且每个不可达的Master至少有一个Slave也在“大分区”端,且集群在小部分Nodes失效后仍然可以恢复有效性。举个例子:

集群有N个Master,且每个Master都有一个Slave,那么集群的可用性只能容忍一个Master节点被分区隔离,也就是说只有一个Master处于小分区端,当第二个Master节点被分区隔离之前扔保持可用性的概率为1-(1 /(N2-1)),这里的意思是:当第一个节点失效后,剩余N2-1节点,此时没有Slave的Master失效的概率为1 /(N2-1)。比如有10个节点,每个Master有一个Slave,当2个nodes被隔离或失效后,集群可用性的概率是:1/(102-1)=5.26%,此时集群不再可用。

为了避免上述情况发生,Redis Cluster提供了“replicas migration”机制,当Master节点发生failover后,集群会动态重新分配、平衡Slaves的分布,有效地提高了集群的可用性。

二、从节点选举逻辑

  1. 节点是已下线Master对应的Slave

  2. FAIL状态的Master负责的hash slot 非空

  3. 主从节点之间的replication link断线的时长不能超过 NODE_TIMEOUT*REDIS_CLUSTER_SLAVE_VALIDITY_MULT

当slave发现自己的master变为FAIL状态时,便尝试进行Failover,以期成为新的master。由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程, 其过程如下:

  1. slave发现自己的master变为FAIL

  2. 将自己记录的集群currentEpoch加1,并广播FAILOVERAUTHREQUEST信息

  3. 其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVERAUTHACK,对每一个epoch只发送一次ack

  4. 尝试failover的slave收集FAILOVERAUTHACK

  5. 超过半数后变成新Master

  6. 广播Pong通知其他集群节点。

从节点并不是在主节点一进入 FAIL 状态就马上尝试发起选举,而是有一定延迟,一定的延迟确保我们等待FAIL状态在集群中传播,slave如果立即尝试选举,其它masters或许尚未意识到FAIL状态,可能会拒绝投票。

延迟计算公式: DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms

SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。

跳转重定位

[root@localhost 8001]# /usr/local/redis/redis-5.0.2/src/redis-cli -c -h 192.168.5.100 -p 8003192.168.5.100:8003> get name-> Redirected to slot [5798] located at 192.168.5.100:8002"xxx"192.168.5.100:8002>

三、集群可视化工具

CodisManager、RedisPlus

四、总结

Redis集群为了解决什么问题而存在的? 解决线性可扩展性。

Redis集群诞生以前怎么解决这个问题? 客户端分片、代理协助分片(Twemproxy)、查询路由、预分片、一致性哈希、客户端代理/转发等。

Redis集群采用什么方式保证线性可扩展性、可用性、数据一致性? Hash槽、查询路由、节点互联的混合模式。

Redis集群化面临的问题是什么? Redis集群本身要解决的是可伸缩问题,同时数据一致、集群可用等一系列问题。前者涉及到了节点的哈希槽的分配(含重分配),节点的增删,主从关系指定与变更(含自动迁移)这些具体的交互过程;后者则是故障发现,故障转移,选举过程等详细的过程。

Redis集群实现的核心思想和思路是什么? 通过消息的交互(Gossip)实现去中心化(指的是集群自身的实现,不是指数据),通过Hash槽分配,实现集群线性可拓展。


参考资料:

  1. https://segmentfault.com/p/1210000009708869/read

  2. https://www.jianshu.com/p/e6894713a6d5