redis 实现高可用的方式分为 主从模式、哨兵模式、集群模式(cluster)
表现为1个主节点,多个从节点,主节点负责读和写,从节点只能读。
在主从架构中,master负责接收写请求,写操作成功后返回客户端OK,然后后将数据异步的方式发送给多个slaver进行数据同步,不过从redis 2.8开始,slave节点会周期性地确认自己每次复制的数据量。
当启动一个slave 节点的时候,它会发送一个PSYNC命令给master 节点。如果slave 节点是重新连接master 节点,那么master 节点仅仅会复制给slave部分缺少的数据; 否则如果是slave 节点第一次连接master 节点,那么会触发一次全量复制(full resynchronization)
开始全量复制的时候,master会启动一个后台线程,开始生成一份RDB快照文件,同时还会将从客户端收到的所有写命令缓存在内存(内存缓冲区)中。RDB文件生成完毕之后,master会将这个RDB发送给slave,slave会先写入本地磁盘,然后再从本地磁盘加载到内存中。然后master会将内存中缓存的写命令发送给slave,slave也会同步这些数据。
优点
缺点:
主从模式中的主节点本质上还是单个redis实例,因此 主节点的 写 和 存储 性能仍受到单机的限制,主从模式只是提高了读的能力。
主节点如果down机,系统无法提供写操作,建立在从节点上的链接仍然可以进行读操作,后续需要人工干预恢复主节点,或切换某个从节点为主节点(但是连接到原主节点的客户端需要修改配置,且在主节点down机之前发生的写操作,如果没有及时同步到从节点,这部分数据将会丢失或者从原主节点的AOF文件从手工恢复)
主从模式中master挂掉后,系统无法提供写操作,没有人工干预的情况下不能自动选主。为了提高可用性,在主从模式的基础上增加哨兵(sentinel),用来监控各节点情况,当master出现故障时,能自动将一个slave转换为master。哨兵节点是特殊的 Redis 节点,不存储数据。哨兵模式中,一般是一主多从加上3个及以上哨兵节点。
关于哨兵的功能描述:
监控(Monitoring): 哨兵会不断地检查主节点和从节点是否运作正常。
自动故障转移(Automatic failover): 当 主节点 不能正常工作时,哨兵会开始 自动故障转移操作,它会将失效主节点的其中一个 从节点升级为新的主节点,并让其他从节点改为复制新的主节点
配置提供者(Configuration provider): 客户端在初始化时,通过连接哨兵来获得当前 Redis 服务的主节点地址
通知(Notification): 哨兵可以将故障转移的结果发送给客户端
当master节点down机后进行选主流程:
如果哨兵挂掉了还能进行主从切换吗?
如果哨兵集群只有 2 个实例,此时一个哨兵要想成为 Leader,必须获得 2 票(超过半数),而不是 1 票,所以,如果有个哨兵挂掉了,那么此时的集群是无法进行主从库切换的。因此通常我们至少会配置 3 个哨兵实例。
优点:
哨兵模式解决了主节点自动切换的问题,实现了高可用性,但是单个节点的写并发能力和存储能力是有上限的,无法扩展,为了解决这个问题才有了cluster,Redis Cluster 集群模式具有高可用、可扩展性、分布式、容错等特性。
redis cluster,主要是针对海量数据+高并发+高可用的场景。至此多个 master ,每个 master都可以挂载多个 slave。这样整个 redis 就可以横向扩容了。如果你要支撑更大数据量的缓存,那就横向扩容更多的 master 节点,每个 master 节点就能存放更多的数据了
Redis Cluster要求至少需要3个master才能组成一个集群,同时每个master至少需要有一个slave节点,当master发生了宕机,就会将对应的slave节点提拔为master,来重新对外提供服务。在cluster模式下集群能够自动将数据进行分片,每个 master 上放一部分数据,而且即使某个master不可用了,其他的master仍然可以提供读写服务。
假如一个key过来了,它要放到master1,master2还是master3上,如果决定的呢?
再此之前,先要介绍一下分布式情况下的数据分区算法:
1. 哈希算法(又称传统哈希算法): 首先就是对key计算出一个hash值,然后对节点数(master个数)取模,然后存放在不同的 master 节点上,例如hash=1,master=3,取模后1%3=1,结果就是将key存放在master1上。算法实现起来很简单,但是有一定的局限性:
对于分布式缓存这种的系统而言,映射规则失效就意味着之前缓存的失效,若同一时刻出现大量的缓存失效,则可能会出现 “缓存雪崩”,这将会造成灾难性的后果。要解决此问题,我们必须在其余节点上重新分配所有现有键,这可能是非常昂贵的操作,并且可能对正在运行的系统产生不利影响。当然还有另一种更好的方案即使用一致性哈希算法。
2. 一致性哈希算法: 是一种特殊的哈希算法,在移除或者添加一个服务器时,能够尽可能小地改变已存在的服务请求与处理请求服务器之间的映射关系,一致性哈希解决了传统哈希算法在分布式哈希表(Distributed Hash Table,DHT)中存在的动态伸缩等问题。
一致性哈希算法是在(传统)哈希算法基础上提出的,在动态变化的分布式环境中,哈希算法应该满足的几个条件:平衡性、单调性和分散性。
一致性哈希算法通过一个叫作一致性哈希环的数据结构实现。这个环的起点是 0,终点最大是 2^32 - 1,并且起点与终点连接,故这个环的整数分布范围是 [0, 2^32-1]
数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的节点,这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响。
但一致性哈希也存在一些问题,致性哈希算法在节点太少时,容易因为节点分布不均匀而造成缓存热点的问题,为了解决这种热点问题,一致性 hash 算法引入了虚拟节点机制,即对每一个节点计算多个 hash,每个计算结果位置都放置一个虚拟节点。这样就实现了数据的均匀分布,负载均衡。
有了上面的基础,我们来看一下 redis cluster 的实现原理,redis cluster 有固定的 16384 个 hash槽(hash slot),对每个 key 计算 CRC16 值,然后对 16384 取模,可以获取 key 对应的槽位。
redis cluster 中每个 master 都会持有部分槽位t,如上图中node1,node2,node3,node4,node5这些节点及它们所对应的槽位。 hash槽 让 节点 的增加和移除很简单,增加一个 master,就将其他 master 的 hash槽 移动部分过去,减少一个 master,就将它的 hash槽 移动到其他 master 上去。移动 hash slot 的成本是非常低的