【Redis】10.哨兵模式与分片集群

文章目录

  • 1. Redis哨兵模式
    • 1.1 搭建哨兵集群
    • 1.2 选举规则与故障转移
    • 1.3 RedisTemplate的哨兵模式
  • 2. Redis分片集群
    • 2.1 搭建分片集群
    • 2.2 散列插槽
    • 2.3 集群伸缩
    • 2.4 故障转移
      • 2.4.1 自动故障转移
      • 2.4.2 手动故障转移
    • 2.5 RedisTemplate访问分片集群

1. Redis哨兵模式

在主从集群模式下,salve节点宕机后可以找master节点同步数据,但是倘若master节点宕机后怎么办?

Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:

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

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

      • 主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线
      • 客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。
      • 【Redis】10.哨兵模式与分片集群_第1张图片
  • 自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主。

    • 当哨兵监测到master宕机,会自动将slave切换成master,然后通过发布订阅模式通知其他的从服务器,修改配置文件,让它们切换主机。
  • 通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端。

【Redis】10.哨兵模式与分片集群_第2张图片

并且单个哨兵对Redis服务器进行监控,可能会出现问题,因此可以使用多个哨兵进行监控。各个哨兵之间还会互相监控,这样就形成了多哨兵模式


1.1 搭建哨兵集群

这里准备使用三个节点作为Sentinel集群,用来监管之前的Redis主从集群。

三个sentinel实例信息如下:

节点 IP PORT
s1 192.168.150.101 27001
s2 192.168.150.101 27002
s3 192.168.150.101 27003

准备一个sentinel.conf的配置文件

port 27001
sentinel announce-ip 192.168.150.101
sentinel monitor mymaster 192.168.150.101 7001 2
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
dir "/tmp/s1"
  • port 27001:是当前sentinel实例的端口
  • sentinel monitor mymaster 192.168.150.101 7001 2:指定主节点信息
    • mymaster:主节点名称,自定义,任意写
    • 192.168.150.101 7001:主节点的ip和端口
    • 2:选举master时的quorum值

另外两个sentinel也需要准备这个配置文件,只是需要把端口号改了。

然后启动三个redis实例

# 第1个
redis-sentinel s1/sentinel.conf
# 第2个
redis-sentinel s2/sentinel.conf
# 第3个
redis-sentinel s3/sentinel.conf

这样哨兵集群的搭建完成了。

哨兵启动后只指定了master的地址,要想知道整个集群中完整的拓扑关系怎么做呢?

哨兵每隔10s就会向每个master节点发送info命令,info命令返回的信息中,包含了主从拓扑关系,其中包括每个slave的地址和端口号。有了这些信息后,哨兵就会记住这些节点的拓扑信息,在后续发生故障时,选择合适的slave节点进行故障恢复。

那么哨兵之间是怎样通信的呢?

哨兵之间是通过Redis提供的发布(pub)/订阅(sub)机制完成的。哨兵节点不会直接与其他哨兵节点建立连接,而是首先会和主库建立起连接,然后向一个名为"sentinel:hello"频道发送自己的信息(IP 和端口),其他订阅了该频道的哨兵节点就会获取到该哨兵节点信息,从而哨兵节点之间互知。


1.2 选举规则与故障转移

一旦master故障,sentinel就需要在多个salve中选择一个作为新的master,选择规则如下:

  1. 首先判断salve节点与master断开时间长短,如果超过指定值(down-after-milliseconds * 10)则会排除该slave节点
  2. 然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举
  3. 如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
  4. 最后是判断slave节点的运行id大小,越小优先级越高。

简而言之,就是slave-priority配置 > 数据完整性 > runid较小者进行选择

选举出新的master之后,就要开始进行故障转移了,步骤如下:

  1. sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master
  2. sentinel给所有其它slave发送slaveof 192.168.150.101 7002 命令,让这些slave成为新master的从节点,开始从新的master上同步数据。
  3. 最后,sentinel将故障节点标记为slave,当故障节点恢复后会自动成为新的master的slave节点

【Redis】10.哨兵模式与分片集群_第3张图片


1.3 RedisTemplate的哨兵模式

在Java中使用哨兵模式,需要以下步骤:

  1. 在pom文件中引入redis的starter依赖:

    
    <dependency>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-data-redisartifactId>
    dependency>
    
  2. 然后在配置文件application.yml中指定sentinel相关信息:

    spring:  
    	redis:    
    		sentinel:      
    			master: mymaster # 指定master名称      
    			nodes: # 指定redis-sentinel集群信息        
    				- 192.168.150.101:27001       
    				- 192.168.150.101:27002       
    				- 192.168.150.101:27003
    
  3. 配置主从读写配置

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

**这里的ReadFrom是配置Redis的读取策略,是一个枚举,**包括下面选择:

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

这样配置完之后,客户端就能感知到主节点发生改变了。

那么,主节点发生变化的时候,客户端是如何感知的呢?

这是基于redis提供的发布(pub)/订阅(sub)机制完成的,客户端可以从哨兵订阅消息,故障转移后,客户端会收到订阅消息。


2. Redis分片集群

主从哨兵模式可以更好解决高可用、高并发读的问题。但是面对高并发写与海量数据存储的问题,这种模式依然存在问题

因此,分片集群模式的出现,就能解决上述出现的问题。

分片集群特征:

  • 集群中有多个master,每个master保存不同数据

  • 每个master都可以有多个slave节点

  • master之间通过ping监测彼此健康状态

  • 客户端请求可以访问集群任意节点,最终都会被转发到正确节点


2.1 搭建分片集群

这里就搭建3个master节点,每个master包含一个salve节点。

【Redis】10.哨兵模式与分片集群_第4张图片

信息如下:

IP PORT 角色
192.168.150.101 7001 master
192.168.150.101 7002 master
192.168.150.101 7003 master
192.168.150.101 8001 slave
192.168.150.101 8002 slave
192.168.150.101 8003 slave

准备一个redis.conf文件

port 6379
# 开启集群功能
cluster-enabled yes
# 集群的配置文件名称,不需要我们创建,由redis自己维护
cluster-config-file /tmp/6379/nodes.conf
# 节点心跳失败的超时时间
cluster-node-timeout 5000
# 持久化文件存放目录
dir /tmp/6379
# 绑定地址
bind 0.0.0.0
# 让redis后台运行
daemonize yes
# 注册的实例ip
replica-announce-ip 192.168.150.101
# 保护模式,不需要密码就可以进去
protected-mode no
# 数据库数量
databases 1
# 日志
logfile /tmp/6379/run.log

每个实例都修改成自己的端口号,然后启动实例。

【Redis】10.哨兵模式与分片集群_第5张图片

虽然服务启动了,但是目前每个服务之间都是独立的,没有任何关联。

在Redis5.0以后,集群管理已经集成到redis-cli中

redis-cli --cluster create --cluster-replicas 1 192.168.150.101:7001 192.168.150.101:7002 192.168.150.101:7003 192.168.150.101:8001 192.168.150.101:8002 192.168.150.101:8003

命令说明:

  • redis-cli --cluster或者./redis-trib.rb:代表集群操作命令
  • create:代表是创建集群
  • --replicas 1或者--cluster-replicas 1 :指定集群中每个master的副本个数为1,此时节点总数 ÷ (replicas + 1) 得到的就是master的数量。因此节点列表中的前n个就是master,其它节点都是slave节点,随机分配到不同master

这样集群的搭建完成了。


2.2 散列插槽

在Redis的分片集群中,Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:

【Redis】10.哨兵模式与分片集群_第6张图片

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

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

例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。

【Redis】10.哨兵模式与分片集群_第7张图片

如图,在7001这个节点执行set a 1时,对a做hash运算,对16384取余,得到的结果是15495,因此要存储到103节点。

到了7003后,执行get num时,对num做hash运算,对16384取余,得到的结果是2765,因此需要切换到7001节点

如何将同一类数据固定的保存在同一个Redis实例?

这一类数据使用相同的有效部分,例如key都以{typeId}为前缀


2.3 集群伸缩

redis-cli --cluster还提供了添加节点的命令。

【Redis】10.哨兵模式与分片集群_第8张图片

新建一个redis节点7004,执行下面命令

redis-cli --cluster add-node  192.168.150.101:7004 192.168.150.101:7001

通过命令查看集群状态:

redis-cli -p 7001 cluster nodes

如图,7004加入了集群,并且默认是一个master节点:

【Redis】10.哨兵模式与分片集群_第9张图片

但是此时7004的插槽数为0,任何数据都不可以存储在7004中。

因此,就需要进行转移插槽的操作了

假如想要将num存储在7004节点,就要先看看num的插槽是多少了

image-20210725161241793

如上图所示,num的插槽为2765.

我们可以将0~3000的插槽从7001转移到7004,命令格式如下:

【Redis】10.哨兵模式与分片集群_第10张图片

建立连接:

image-20210725161506241

【Redis】10.哨兵模式与分片集群_第11张图片

这里就是问要移动多少个插槽,这里我想移动3000个

【Redis】10.哨兵模式与分片集群_第12张图片

这里问的是那个node来接收这些插槽??

显然是7004节点,可以查看节点的id

【Redis】10.哨兵模式与分片集群_第13张图片

【Redis】10.哨兵模式与分片集群_第14张图片

这里询问,你的插槽是从哪里移动过来的?

  • all:代表全部,也就是三个节点各转移一部分
  • 具体的id:目标节点的id
  • done:没有了

这里可以选择从7001获取,所以填写7001的id

【Redis】10.哨兵模式与分片集群_第15张图片

填完后,点击done,这样插槽转移就准备好了:

最后输入yes,就可以了

再次查看,可以看见插槽分配成功了

【Redis】10.哨兵模式与分片集群_第16张图片


2.4 故障转移

对于宕机的master进行故障转移,有以下两种方式:

  • 自动故障转移
  • 手动故障转移

2.4.1 自动故障转移

自动故障转移的话,当一个redis宕机

首先先是疑似宕机

image-20210725162319490

然后确认下线,自动升级一个salve为新的master

image-20210725162408979

当7002再次启动,就会变为一个slave节点了:

image-20210727160803386


2.4.2 手动故障转移

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

【Redis】10.哨兵模式与分片集群_第17张图片

这种failover命令可以指定三种模式:

  • 缺省:默认的流程,如图1~6歩
  • force:省略了对offset的一致性校验
  • takeover:直接执行第5歩,忽略数据一致性、忽略master状态和其它master的意见

2.5 RedisTemplate访问分片集群

RedisTemplate底层同样基于lettuce实现了分片集群的支持,而使用的步骤与哨兵模式基本一致:

1)引入redis的starter依赖

2)配置分片集群地址

3)配置读写分离

与哨兵模式相比,其中只有分片集群的配置方式略有差异,如下:

spring:
  redis:
    cluster:
      nodes:
        - 192.168.150.101:7001
        - 192.168.150.101:7002
        - 192.168.150.101:7003
        - 192.168.150.101:8001
        - 192.168.150.101:8002
        - 192.168.150.101:8003

参考:

  • 黑马程序员Redis入门到实战教程,深度透析redis底层原理+redis分布式锁+企业解决方案+黑马点评实战项目
  • Redis哨兵面试题(高频面试题)- Java程序鱼

你可能感兴趣的:(#,Redis,redis,数据库,java)