前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis系列之高可用集群模式介绍

Redis系列之高可用集群模式介绍

作者头像
SmileNicky
发布2024-05-24 13:46:04
630
发布2024-05-24 13:46:04
举报
文章被收录于专栏:Nicky's blogNicky's blog

Redis系列之高可用集群模式介绍

1. Redis主从模式

1.1 什么是主从模式?

主从模式,是redis集群最基本的模式,主库负责读写,从库负责读。主库的数据会同步到从库,但是从库写的数据不会自动同步到主库,除非用写脚本等方式手动同步。这种模式应急能力比较差,假如出现宕机的情况,需要手动进行修改

1.2 全量同步

  1. master服务端收到slave的同步命令psync后,判断slave传过来的master_replid是否和mastermaster_replid一致,如果不一致或者传的是一个空的,就需要进行全量同步
  2. master开始执行bgsave命令,生成一个RDB文件,生成成功后,给到slave,并且将master_replidofferset传过去
  3. slave收到RDB文件后,清空slave自己内存中的数据,然后通过RDB文件重新加载数据

补充细节:

Redis4.0之前的版本,slavemaster传的不是master_replid,而是runid,但是runid每次实例重启都会改变,也就是会进行一次全量同步,所以Redis4.0版本后采用replid

master进行bgsave生成RDB文件时,因为bgsave命令是异步的,所以在同步时候,可能会接收其它指令,所以这些指令会先暂存在一个内存空间replication_buffer,等到slave加载完RDB文件后,再同步给slave

设置replication_buffer大小可以通过如下进行配置

代码语言:javascript
复制
 # 256mb:硬性限制,大于256M断开连接;64mb 60:软限制,超过64M并且超过了60s还没进行同步就会断开连接
client-output-buffer-limit replica 256mb 64mb 60

内存空间replication_buffer不能设置太小,如果设置太小,超过了大小之后,为了数据安全,会关闭master跟从库的连接,再次连接就得重新全量同步,但是问题还在,可能导致无限同步问题

1.3 增量同步

有全量同步就会有增量同步的,那么redis什么时候会进行增量同步?增量同步的流程是怎么样的?

在全量同步时候说过,master会给slave一个偏移量offerset,如果出现slavemaster网络断开一小会等情况,导致偏移量跟master差了一点,网络恢复,slave重新跟master连接后,这时slave会发送指令,判断是否需要增量同步还是全量同步?

  1. master会判断slave传过来的master_replid是否一致,如果一致,满足条件,进行下一步校验
  2. 增量同步会根据偏移量offerset和积压缓存数据来判断,增量同步会去积压缓存replication_backlog_buffer获取数据,如果偏移量只差了3条数据,同时在积压缓存里查找得到,就会进行增量同步

补充细节:

master_replid一致的情况,可能也不会进行增量同步,因为积压缓存的数据,可能是被覆盖了,导致需要增量同步的数据,查找不到,为什么会被覆盖?因为积压缓存是可以设置大小的,如果内存满了,就会覆盖之前的数据

设置积压缓存replication_backlog_buffer的大小

代码语言:javascript
复制
repl-backlog-size 1mb

2. Redis哨兵模式

2.1 什么是哨兵模式?

Redis的主从模式是可以解决负载、数据备份等问题,但是,如果master宕机的情况,slave是不会自动升级为master的,必须手动升级,所以就有了哨兵集群的方案,以及后面介绍的cluster集群

先看看官网Sentinel的介绍

大概意思是Redis Sentinel在不适用Cluster集群的时候,为Redis提供了高可用性,并且提供了检测、通知、自动故障转移、配置提供等功能

监控 :能够监控我的redis实例是否正常运行

通知:如果redis的实例出现问题,能够通知给其它实例以及其它Sentinel

自动故障转移master宕机,slave可以自动升级为master

配置提供Sentinel可以提供Redis的master实例地址,那么客户端只需要跟Sentinel进行连接,master宕机了会提供新的master

总而言之,Sentinel是独立于Redis服务的单独服务,并且它们之间是相互通信的,可以画图表示一下Redis的哨兵模式:

2.2 故障转移流程

下面说一下,master宕机的情况,哨兵是怎么去监控的,并且怎么选举slave作为新的master的,首先,各Sentinel之间,以及Sentinelmaster/slave之间都会进行通信的,Redis故障转移的过程大致为:

  • 发现master故障
    1. Sentinel是会一直和master通信的,默认是1s发送ping,当某个Sentinel发现在一定时间内(down-after-milliseconds)没有收到master的有效回复,这个Sentinel就会认为这个master宕机了,这个时候还不会触发故障转移,只会标记一个SDOWN(Subjectively Down condition)状态,也就是我们讲的主观下线
    2. 之后这个sentinel会去询问其它的sentinel能否连上master,如果超过法定人数quorum都认为master不可用,都标记SDOWN状态,那么就会将master标记为ODOWN(Objectively Down condition)客观下线
  • 选举一个Sentinel来故障转移
    1. 因为Sentinel一般是集群的,所以需要选举一个Sentinel来进行故障转移就好,并且这个Sentinel在做故障转移的时候,其它Sentinel不能进行故障转移
    2. 选举这个Sentinel,有两个判断因素
      • 如果Quorum如果小于等于sentinel数量的一半,那么必须超过半数的sentinel授权,这个sentinel才可以去做故障转移,不如有5台sentinel,配置的quornum为2,那么选举时候必须有3台以上的sentinel授权
      • 如果Quorum大于Sentinel数量的一半,那么必须QuorumSentinel授权,故障迁移才能启动
  • 选举slave转变为master 选出一个Sentinel来做故障转移后,具体选举哪个Slave来当master,需要由各种因素综合考虑:
    1. 与master的断开连接时间 如果slave与主服务器断开时间超过主服务器配置的超时时间(down-after-milliseconds)的十倍,被认为不适合成为master,直接去除资格
    2. 配置的优先级 配置replica-priorityreplica-priority越小,优先级越高,但是配置为0的时候,永远没有资格升级为master,具体参考https://redis.io/docs/manual/sentinel/#replica-selection-and-priorit
    3. 已复制的偏移量 数据最新的优先升级为master
    4. Run ID 每个实例启动都会有一个RunId,可以通过info server查看

2.3 分区下的一致性

Redis集群(分区)后,可能会有一致性的问题,也可以说是脑裂问题,其实就是有2个master,client会从不同的master写数据,从而在master恢复的时候,会导致数据丢失。

举个例子,一个集群的架构是有一个master,下面还有两个slave,假如出现masterslave连接不上,这时候就会选举,重新选举一个slave作为master,等到网络恢复后,原先的master恢复连接,在这种集群架构里肯定不能有两个master,所以会恢复原先的master地位,新选举的master变为slave,显而易见这种情况会造成数据丢失了,因为故障过程,数据还可以继续写到原先的master,一恢复网络,这个时间段写的数据都会丢失了,所以有什么方法可以避免这种情况?

首先对于这种问题,不能解决,只能避免,避免数据丢失的情况,在Redis官网给出了一种方案,需要在Redis.cfg文件中加上配置:

代码语言:javascript
复制
# 至少有1个从节点同步到我主节点的数据,这样配置就可以避免原先断网的master进行数据写入,减少数据一致性问题
min-replicas-to-write 1
# 判断上面1个的延时时间必须小于等于10s
min-replicas-max-lag 10

3. Reids Cluster模式

3.1 什么是Redis Cluster模式?

redis的哨兵模式提供了比如监控、自动故障转移等高可用方案,但是这种方案,容量相对固定,要进行持续扩容或者数据分片就不适合,所以有另外一种更复杂的集群方案,Cluster集群模式。

Cluster集群模式,Cluster模式支持多主多从,这种模式,按照key进行虚拟槽位分配,使得key分配到不同的主节点,使用这种模式使得集群节点有更大的容量,也可以持续进行扩容,如果主库节点出现宕机,也会从从库节点选出一个新的主库节点

所以,如果redis的数据量不是很大,就可以使用哨兵模式,如果Redis存储的数据量比较大,而且需要持续扩容,那么就需要选择Cluster模式

3.2 Redis Cluster特点

  • 多个节点之间按照key进行虚拟槽分配,也就是我们说的数据分片
  • 当某些节点遇到故障的时候,其他节点还能继续服务

3.3 hash slot虚拟槽

那么如何进行数据分片?数据分片相当于数据库的分表,不同的数据放到不同的表里。数据分片就是要把不同的数据放到不同的实例里面。

所以,在Redis里面提出一个Hash槽,也可以称之为虚拟槽的概念。什么是虚拟槽?其实就是虚拟节点,Redis Cluster中有16384个虚拟槽。

key和虚拟槽怎么对应?会根据CRC16取模16383得到一个0到16383的值,计算公式是:slot = CRC16(key) & 16383,通过这个公式计算得到的值就表示key在哪个虚拟槽,举例:

代码语言:javascript
复制
set k1 1: 
CRC16(k1) & 16383 = 10

set k2 1:
CRC16(k1) & 16383 = 5468

set k3 1:
CRC16(k1) & 16383 = 10988

# 所以k1、k2、k3对应的槽位分别是10、5468、10988

如果有3台主库,对应的槽位分别为

代码语言:javascript
复制
master1 0-5460虚拟槽
master2 5461-10922虚拟槽
master3 10923-16383虚拟槽

则,k1、k2、k3分别会放在master1、master2、master3

为什么使用16384个虚拟槽?

Redis的官方issues也有给出答复,作者的亲自答复,请看原文:

翻译下:

  • 正常的心跳包携带节点的完整配置,可以用旧的方式以幂等的方式替换,以更新旧的配置,这意味着它们包含原始形式的节点插槽配置,该节点使用2k空间和16k插槽,但如果使用65k插槽会使用令人望而止步的8k空间
  • 同时,由于其他设计权衡,Redis Cluster不太可能扩展到1000多个主节点

看了大概意思是使用这个值是基于应用场景考虑,一般Cluster不太可能扩展到1000多个主节点,所以使用2k空间和16k的插槽比较合理,也不会占用太大空间,有读者还可能不太理解,所以继续分析,在redis中,cluster节点是会相互监测进行数据交互的,所以看下交互的数据头文件,cluster.h

代码语言:javascript
复制
typedef struct {
    char sig[4];        /* Signature "RCmb" (Redis Cluster message bus). */
    uint32_t totlen;    /* Total length of this message */
    uint16_t ver;       /* Protocol version, currently set to 1. */
    uint16_t port;      /* TCP base port number. */
    uint16_t type;      /* Message type */
    uint16_t count;     /* Only used for some kind of messages. */
    uint64_t currentEpoch;  /* The epoch accordingly to the sending node. */
    uint64_t configEpoch;   /* The config epoch if it's a master, or the last
                               epoch advertised by its master if it is a
                               slave. */
    uint64_t offset;    /* Master replication offset if node is a master or
                           processed replication offset if node is a slave. */
    char sender[CLUSTER_NAMELEN]; /* Name of the sender node */
    unsigned char myslots[CLUSTER_SLOTS/8];
    char slaveof[CLUSTER_NAMELEN];
    char myip[NET_IP_STR_LEN];    /* Sender IP, if not all zeroed. */
    char notused1[34];  /* 34 bytes reserved for future usage. */
    uint16_t cport;      /* Sender TCP cluster bus port */
    uint16_t flags;      /* Sender node flags */
    unsigned char state; /* Cluster state from the POV of the sender */
    unsigned char mflags[3]; /* Message flags: CLUSTERMSG_FLAG[012]_... */
    union clusterMsgData data;
} clusterMsg;

源码里有个char类型的变量unsigned char myslots[CLUSTER_SLOTS/8];,表示的意思是为当前槽的数量除以8,因为一个char类型在c语言中大小被定义为1Byte,所以大小为16384/8/1024=2kb,所以使用16384个虚拟槽的话,只需占用2kb的空间

但是为什么作者要举例65k?因为crc16算法得到的hash值是16bit,最大的值为65536,计算占用空间达到了8kb,所以数据传输会比较慢,所以一般场景16384个虚拟槽就符合需求,就取了一个16384的值,提升传输性能

补充知识点:

  1. bit(位,又名“比特”):bit的缩写是b,是计算机中的最小数据单位(属于二进制的范畴,其实就是0或者1)
  2. Byte(字节):Byte的缩写是B,是计算机文件大小的基本计算单位。比如一个字符就是1Byte,如果是汉字,则是2Byte。 1B(字节)=8b(位) 1 KB = 1024 B 1 MB = 1024 KB 1 GB = 1024 MB 1TB = 1024GB
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-05-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. Redis主从模式
    • 1.1 什么是主从模式?
      • 1.2 全量同步
        • 1.3 增量同步
        • 2. Redis哨兵模式
          • 2.1 什么是哨兵模式?
            • 2.2 故障转移流程
              • 2.3 分区下的一致性
              • 3. Reids Cluster模式
                • 3.1 什么是Redis Cluster模式?
                  • 3.2 Redis Cluster特点
                    • 3.3 hash slot虚拟槽
                    相关产品与服务
                    云数据库 Redis
                    腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
                    领券
                    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档