【Redis——主从复制、哨兵机制、分片集群_底层原理】

一、主从复制

1、主从第一次同步——》全量同步

  •  第一阶段:当slave和master建立连接后,会向master请求数据同步,master经过一些判断方式后知道了slave是第一次和我进行数据同步,就会将master当前的数据版本信息发送给slave,slave收到版本信息后进行保存。
  • 第二阶段:master执行bgsave命令,创建一个子进程去执行RDB操作,将当前内存中的所有数据生成RDB文件,生成后将RDB文件发送给slave,slave拿到RDB文件后,就会清除本地数据,加载RDB文件当中的数据,但是,在子进程生成RDB文件以及发送RDB文件的过程当中,一旦master又有新的数据改动,此时master就会记录在这个RDB期间的所有命令-》存在一个repl_baklog文件中。
  • 第三阶段:在记录完repl_baklog之后将这个文件再此发送给slave,slave拿到这个文件后,执行文件当中的命令,就可以保证与master当中数据一致,后续master只需要将最新生成的repl_baklog文件发送给slave,slave执行该命令即可。

2、master如何判断slave是第一次同步

有几个概念,可以作为判断依据:

  • Replication Id:简称replid,是数据集的标记,id一致则说明是同一数据集。每一个master都有唯一的replid,slave则会继承master节点的replid

  • offset:偏移量,随着记录在repl_baklog中的数据增多而逐渐增大。slave完成同步时也会记录当前同步的offset。如果slave的offset小于master的offset,说明slave数据落后于master,需要更新。

因此slave做数据同步,必须向master声明自己的replication id 和offset,master才可以判断到底需要同步哪些数据。

 因为slave原本也是一个master,有自己的replid和offset,当第一次变成slave,与master建立连接时,发送的replid和offset是自己的replid和offset。

master判断发现slave发送来的replid与自己的不一致,说明这是一个全新的slave,就知道要做全量同步了。master会将自己的replid和offset都发送给这个slave,slave保存这些信息。以后slave的replid就与master一致了。

因此,master判断一个节点是否是第一次同步的依据,就是看replid是否一致

 完整流程描述:

  • slave节点请求增量同步

  • master节点判断replid,发现不一致,拒绝增量同步

  • master将完整内存数据生成RDB,发送RDB到slave

  • slave清空本地数据,加载master的RDB

  • master将RDB期间的命令记录在repl_baklog,并持续将log中的命令发送给slave

  • slave执行接收到的命令,保持与master之间的同步

3、增量同步 

全量同步需要先做RDB,然后将RDB文件通过网络传输个slave,成本太高了。因此除了第一次做全量同步,其它大多数时候slave与master都是做增量同步

什么是增量同步?就是只更新slave与master存在差异的部分数据。

 

 master如何知道slave与自己的数据差异在哪里?repl_baklog文件会记录master的数据改动,这个文件是一个固定大小的数组,且是环形数组。也就是说角标到达数组末尾后,会再此从0开始读写。这样数组头部的数据就会被覆盖。

repl_baklog中会记录Redis处理过的命令日志及offset,包括master当前的offset,和slave已经拷贝到的offset:

 slave与master的offset之间的差异,就是salve需要增量拷贝的数据了。

随着不断有数据写入,master的offset逐渐变大,slave也不断的拷贝,追赶master的offset:

 直到数组被填满:

 此时,如果有新的数据写入,就会覆盖数组中的旧数据。不过,旧的数据只要是绿色的,说明是已经被同步到slave的数据,即便被覆盖了也没什么影响。因为未同步的仅仅是红色部分。

但是,如果slave出现网络阻塞,导致master的offset远远超过了slave的offset:

 如果master继续写入新数据,其offset就会覆盖旧的数据,直到将slave现在的offset也覆盖:

棕色框中的红色部分,就是尚未同步,但是却已经被覆盖的数据。此时如果slave恢复,需要同步,却发现自己的offset都没有了,无法完成增量同步了。只能做全量同步。

 

4、主从同步优化

 

可以从以下几个方面来优化Redis主从就集群:

  • 在master中配置repl-diskless-sync yes启用无磁盘复制,避免全量同步时的磁盘IO。

       什么意思呢?我们上述的全量同步,都是先生成RDB文件,RDB文件是需要经过磁盘IO的,生成后将该RDB文件发送给slave,磁盘IO是一项消耗非常大的操作,因此可以采用直接将数据写到网络当中,由网络直接发送,而不再进行磁盘的IO。

  • Redis单节点上的内存占用不要太大,减少RDB导致的过多磁盘IO

  • 适当提高repl_baklog的大小,发现slave宕机时尽快实现故障恢复,尽可能避免全量同步

  • 限制一个master上的slave节点数量,如果实在是太多slave,则可以采用主-从-从链式结构,减少master压力

二、哨兵机制

slave节点宕机恢复后可以找master节点同步数据,master节点宕机后怎么办?哨兵机制——》选举master的从节点作为主节点!

哨兵结构图:

1、哨兵的作用

  • 监控:Sentinel 会不断检查您的master和slave是否按预期工作

  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主

  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端

2、集群监控原理

Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:

  • 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线
  • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。

3、新master选举

一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:

  • 首先会判断slave节点与master节点断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点

  • 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举

  • 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高

  • 最后是判断slave节点的运行id大小,越小优先级越高。

4、集权故障恢复

当选出一个新的master后,该如何实现切换呢?

流程如下:

  • sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master

  • sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。

  • 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点

5、Spring当中配置主从读写分离

 在项目的启动类中,添加一个新的bean:

@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
    return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}

这个bean中配置的就是读写策略,包括四种:

  • MASTER:从主节点读取

  • MASTER_PREFERRED:优先从master节点读取,master不可用才读取replica

  • REPLICA:从slave(replica)节点读取

  • REPLICA _PREFERRED:优先从slave(replica)节点读取,所有的slave都不可用才读取master

三、分片集群

围绕以下这张图展开描述:

1、什么是Redis分片集群

所谓Redis集群,就是将0-16383个槽均分给集群当中的物理节点,所谓物理节点,就是我们之前所说的主节点,即对外提供写服务的节点,注意,此时的主节点可能不止一个。后续的客户端进行操作时,并非直接去操作某个节点,而是经过集群处理过后再去操作某个节点,这样一来,减少了单个节点的压力,类似于负载均衡的原理。

2、为什么是0-16383个槽?而不是整数个或者其他数字?

数据key不是与节点绑定,而是与插槽绑定。redis会根据key的有效部分计算插槽值,分两种情况:

  • key中包含"{}",且“{}”中至少包含1个字符,“{}”中的部分是有效部分

  • key中不包含“{}”,整个key都是有效部分

计算方式:利用CRC16算法对key进行计算得到一个hash值,然后对16384取余,得到的结果就是slot值。

Redis集群当中采用了CRC16算法,这个算法有如下特点:

①、对集群模式下的所有key进行CRC16计算,计算的结果始终在0-16383这个范围内。

②、对客户端的key进行CRC16计算时,同一个key多次进行计算,结果始终一致。

③、对客户端的不同key进行CRC16计算,计算的结果会出现不同的key的计算结果一致的情况。

基于以上的特点,因此我们可以得出结论:我们可使用的槽有16384个。

3、客户端需要操作Redis时,具体流程是什么样的? 

以图为例:

Node-1节点持有0~5000槽位

Node-2节点持有5001~8000槽位

Node-3节点持有8001~13000槽位

Node-4节点持有13001~16383槽位

问题1:说好的均分槽位呢?

此处所说的均分,并非是我们常规理解意义上的用16384除以节点个数,而是均匀的分配,使其槽位都分配出去。

问题2:节点个数有没有要求?

如果是16385个节点,那槽位不就没得分了?总得有一个节点没有节点分不到槽,此时,Redis集群就不会搭建成功。并且,我们我们为了保证集群的高可用性,建议每个主节点都至少持有一个从节点,当主节点宕机的时候,从节点可以顶替上来~

问题3:从节点如何顶替主节点?

主节点持有的槽位会转移给从节点,因为从节点上面的数据和主节点的一致,只需要将数据分配进槽位即可。

问题4:在原有集群当中添加新的节点?此时槽位如何分配?

在上图过程当中,我们比如添加Node-5节点,此时Node-5节点假设需要2000个槽位,此时其余四个节点就会拼凑出2000个槽位给Node-5节点,同时槽位当中的值也会转移给Node-5节点,这样一来,Node-5节点就算添加进了集群。

注意:需要保证0-16383个槽位都分布在某个节点上,不会存在槽位未分配的情况。

客户端操作步骤:

①、我们知道客户端操作Redis时,根据客户端持有的key-value数据,CRC16算法对key进行计算,根据计算得出的结果去找到持有这个槽位的节点。

②、找到节点之后,在节点上进行操作

③、集群维护节点-》节点维护槽-》槽维护值

4、分片集群的故障转移机制

①、自动

  1. 该实例与其他实例失去连接
  2. 疑似宕机
  3. 确定下线,自动提升一个slave为新的master
  4. 当该实例再此启动,就会变成一个slave节点了。

②、手动

利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值