这么全的Redis集群模式演变过程及原理不了解一下?

这里是weihubeats,觉得文章不错可以关注公众号小奏技术,文章首发。拒绝营销号,拒绝标题党

背景

无论是面试还是工作,我们都会遇到Redis集群问题,所以我们一次性将Redis目前支持的各种集群方式都深入了解一下

主从模式

最简单的集群方式,主从库之间采用的是读写分离

  • 读操作: 主库、从库都可以接收
  • 写操作: 首先在主库执行,然后通过RDB(log)方式同步给从库

image-20220217223540702

为什么使用读写分离方式。主要是为了简单实现,读写分离方式可以避免解决多数据同时写入的并发处理问题。

简单举例:如果不采用读写分离方式,三个库同时接收到写入的数据,那么如何保证上个库最后查询的数据的一致性问题呢?是强一致还是弱一致?实现成本很高,也会带来额外的性能开销,而使用读写分离方式就无需考虑这些问题

主从数据同步问题

分布式系统必须要考虑的就是数据同步问题

是同步复制还是异步复制?

异步复制

具体同步过程

image-20220217224109376

  1. 主库和从库建立连接。主库发送连接请求,从库给主库发送psync命令,表示要进行数据同步,主库根据这个命令参数启动复制。psync命令包含了主库的runID和复制进度offset两个参数
  • runnID:每个Redis实例启动都会自动生成的一个随机ID,用来标识这个实例。第一次发送psync命令不知道主库的runnID所以用?代替
  • offset: 复制进度,开始为-1。代表第一次复制
  1. 主库收到psync命令后返回FULLRESYNC相应标识第一次采用全量复制。主库将当前所有的数据都复制给从库
  2. 主库本地执行bgsave生成RDB文件,然后将RDB文件发送给从库,从库在接收到RDB文件先清空当前数据库,然后加载RDB文件中的数据。主库在同步数据过程中仍然可以处理客户端请求。客户端新来的请求会写入到内存中专门的replication buffer中
  3. 最开始的RDB同步完成后就开始同步主库新写入的数据即replication buffer中的数据。这样数据同步就完成了

主-从-从模式

主从有一个明显的缺点就是每次主从同步都需要主库生成全量的RDB文件,同时传输RDB文件。这两个操作还是比较耗时的,如果从库数量很多的话就会占用主库很多的资源,让主库的线程都忙着fork子线程生成RDB文件,影响主线程的正常请求处理。所以就衍生出了主-从-从模式

这种模式简单来说就是在我们部署的时候我们可以设置一个从库的同步从另一个从库来同步数据,就无需所有的从库都从主库同步数据了。所以我们之前的架构图就演变成如下的了

image-20220217225615526

但是这种集群最主要的问题是如果主库挂了怎么办,那么整个集群就处于不可用的状态了,只能我们人工干预了,所以我们又演变出了下面新的集群方式:哨兵集群

哨兵集群

**哨兵(sentinel)集群为了解决主从模式下没有故障转移问题,即没有主从自动切换而产生的集群方式。我们知道我们主从模式下所有的写操作都是基于Master,所以在Master宕机后,所有写操作是不可用的,只能人工去处理,这在高可用中我们是不能接受的,所以主从模式不算真正的高可用,因为没有自动故障转移,只是分担了主库读数据的压力。这里我们来简单看看哨兵(sentinel)**集群是如何处理Master故障问题的

聊到故障切换就不得不聊一聊分布式系统老生常谈的问题

  1. 故障检测
  2. 脑裂问题

这两个问题在我上一篇文章中有详细介绍,这里就不展开讲解了。我们直接从哨兵的工作原理说起

哨兵的工作原理

**哨兵(sentinel)**可以理解为一个Agent,他的主要工作就是两个:选主、监控(心跳检测、故障检测)

我们先看看故障检测是如何实现的

故障检测

哨兵(sentinel)进程在运行的时候,周期性给所有的主从库(整个redis集群)发送PING命令,检测他们是否仍然在线。如果从库没有在设定时间内相应哨兵的PING命令,哨兵就将它标记为下线状态;同样主库也是如此,不过主库的下线还会触发主动切换主库流程

选主

这个选主过程有如下三个步骤:

image-20220217225944267

  • 故障检测:判断主库是否处于下线状态(需要考虑脑裂问题)

​ 判断主库是否下线又分两种

​ 1. 主观下线:**哨兵(sentinel)**会使用PING检测自己和主库(Master)、从库之间的网络情况,判断实例是否存活,如果PING命令超时,则哨兵将实例标记为主观下线

​ 2. 客观下线:多个**哨兵(sentinel)**判断主库(Master)宕机,则将主节点标记为客观下线

image-20220218220105085

客观下线的标准是当有N个哨兵时,最好要有N/2+1个实例判断主库为主观下线,才最终判断主库为客观下线。这种方式主要是为了减少误判。由于哨兵也要保证高可用,一般哨兵都是三台起步,哨兵之间的通信是依赖于Redis的发布订阅功能(pub/sub),通过主库知道其他哨兵机器的IP和端口

  • 选主:需要考虑选用哪个从库作为主库

​ **哨兵(sentinel)**需要选用哪个从库作为新的主库,需要一个评分,评分最高的从库作为新的主库,评分步骤如下

​ 第一轮:用户通过 slave-priority配置项,给从库设置不同的优先级。如果优先级有最高的,则选用优先级最高的从库作为主库。如果没有,则开始第二轮打分

​ 第二轮:和旧主库数据同步最接近的从库成为新的主库,具体的判断依据是依赖于从库中slave_repl_offset这个参数值记录的复制进度

​ 第三轮: 如果第二轮得分一样,则使用ID号最小的从库作为新的主库,这个ID号就是前面随机生成的ID

  • 通知:将新主库发送给从库

一个哨兵(sentinel)成功地对一个master进行了故障转移(failover),它将会把关于master的最新配置通过广播形式通知其它哨兵(sentinel),其它的**哨兵(sentinel)则更新对应master的配置。同时哨兵(sentinel)**会将新主库的信息发送给其他从库,让它们和新主库建立连接,并进行数据复制。**哨兵(sentinel)**也会把新主库的信息广播通知给客户端,让它们把请求操作发到新主库上

切片集群

可以看到前面几种集群方式都没有提升Redis本身的存储空间,只是提升了可用性和并发的压力。如果我们要存储的Key很多,需要很大的内存,我们使用上面的集群方式始终无法突破单机Redis的内存限制,始终只能升级单个Redis实例的资源配置(纵向扩展),但是硬件配置始终是有极限的。所以我们又衍生出了切片集群,用于支持横向扩展

Redis Cluster 原理

Redis Cluster 主要是采用哈希槽(Hash Slot)的方式。首先将整个集群分为16384个哈希槽,然后将需要存储的key和实例都和这些哈希槽映射上。具体的映射步骤如下

首先根据键key按照CRC16算法计算出一个16bit的值,然后用这个16bit的值对16384进行取模。这样key就落到了0~16383这些哈希槽里面了。

实例如何与这些哈希槽对应上呢?假如我们有N个实例,那么每个实例对应的哈希槽就是16384/N 这样就算出了每个实例的哈希槽个数。

image-20220218221723550

客户端如何定位到数据在哪台Redis

客户端可以通过key计算出落在哪个哈希槽上。除此之外客户端还需要知道哪个实例对应有哪些哈希槽。客户端在访问任何一个实例的时候就会获得所有实例的哈希槽信息,同时缓存在自己本地,每次通过计算可以得到对应的哈希槽,哈希槽所对应的实例,就可以去拿到正确的数据了

总结

本次我们主要讨论了Redis的三种集群方式。我们可以看到这么多集群方式不是一下子就全部有的,我们是慢慢演变过来的,有最初的主从,演变为主-从-从,到哨兵,最后的切片。都是为了解决不同的问题。其实这里也是可以看到架构的一个演变过程。当然本文只是粗略的说了一下各种集群方式及原理,还有很多细节没有讨论到,后续有机会可以在一起探讨

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值