在主从集群模式下,salve节点宕机后可以找master节点同步数据,但是倘若master节点宕机后怎么办?
Redis提供了哨兵(Sentinel)机制来实现主从集群的自动故障恢复。哨兵的结构和作用如下:
监控:Sentinel 会不断检查您的master和slave是否按预期工作。
自动故障恢复:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后也以新的master为主。
通知:Sentinel充当Redis客户端的服务发现来源,当集群发生故障转移时,会将最新信息推送给Redis的客户端。
并且单个哨兵对Redis服务器进行监控,可能会出现问题,因此可以使用多个哨兵进行监控。各个哨兵之间还会互相监控,这样就形成了多哨兵模式
这里准备使用三个节点作为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 和端口),其他订阅了该频道的哨兵节点就会获取到该哨兵节点信息,从而哨兵节点之间互知。
一旦master故障,sentinel就需要在多个salve中选择一个作为新的master,选择规则如下:
简而言之,就是slave-priority配置 > 数据完整性 > runid较小者进行选择
选举出新的master之后,就要开始进行故障转移了,步骤如下:
在Java中使用哨兵模式,需要以下步骤:
在pom文件中引入redis的starter依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-redisartifactId>
dependency>
然后在配置文件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
配置主从读写配置
@Bean
public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){
return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED);
}
**这里的ReadFrom是配置Redis的读取策略,是一个枚举,**包括下面选择:
这样配置完之后,客户端就能感知到主节点发生改变了。
那么,主节点发生变化的时候,客户端是如何感知的呢?
这是基于redis提供的发布(pub)/订阅(sub)机制完成的,客户端可以从哨兵订阅消息,故障转移后,客户端会收到订阅消息。
主从哨兵模式可以更好解决高可用、高并发读的问题。但是面对高并发写与海量数据存储的问题,这种模式依然存在问题
因此,分片集群模式的出现,就能解决上述出现的问题。
分片集群特征:
集群中有多个master,每个master保存不同数据
每个master都可以有多个slave节点
master之间通过ping监测彼此健康状态
客户端请求可以访问集群任意节点,最终都会被转发到正确节点
这里就搭建3个master节点,每个master包含一个salve节点。
信息如下:
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
每个实例都修改成自己的端口号,然后启动实例。
虽然服务启动了,但是目前每个服务之间都是独立的,没有任何关联。
在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这样集群的搭建完成了。
在Redis的分片集群中,Redis会把每一个master节点映射到0~16383共16384个插槽(hash slot)上,查看集群信息时就能看到:
**数据key不是与节点绑定,而是与插槽绑定。**redis会根据key的有效部分计算插槽值,分两种情况:
例如:key是num,那么就根据num计算,如果是{itcast}num,则根据itcast计算。计算方式是利用CRC16算法得到一个hash值,然后对16384取余,得到的结果就是slot值。
如图,在7001这个节点执行set a 1时,对a做hash运算,对16384取余,得到的结果是15495,因此要存储到103节点。
到了7003后,执行get num
时,对num做hash运算,对16384取余,得到的结果是2765,因此需要切换到7001节点
如何将同一类数据固定的保存在同一个Redis实例?
这一类数据使用相同的有效部分,例如key都以{typeId}为前缀
redis-cli --cluster还提供了添加节点的命令。
新建一个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节点:
但是此时7004的插槽数为0,任何数据都不可以存储在7004中。
因此,就需要进行转移插槽的操作了
假如想要将num存储在7004节点,就要先看看num的插槽是多少了
如上图所示,num的插槽为2765.
我们可以将0~3000的插槽从7001转移到7004,命令格式如下:
建立连接:
这里就是问要移动多少个插槽,这里我想移动3000个
这里问的是那个node来接收这些插槽??
显然是7004节点,可以查看节点的id
这里询问,你的插槽是从哪里移动过来的?
这里可以选择从7001获取,所以填写7001的id
填完后,点击done,这样插槽转移就准备好了:
最后输入yes,就可以了
再次查看,可以看见插槽分配成功了
对于宕机的master进行故障转移,有以下两种方式:
自动故障转移的话,当一个redis宕机
首先先是疑似宕机
然后确认下线,自动升级一个salve为新的master
当7002再次启动,就会变为一个slave节点了:
利用cluster failover命令可以手动让集群中的某个master宕机,切换到执行cluster failover命令的这个slave节点,实现无感知的数据迁移。其流程如下:
这种failover命令可以指定三种模式:
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程序鱼