IT领域往往都是面试造火箭,实际工作拧螺丝。为了更好的应对面试,让大家能拿到更高的offer✉,我们接下来就讲讲“造火箭”的事情🧑🚀。
🔥🔥🔥 包括以下几方面🔽 🎈:
🌈 - Redis - 高级:
📛 -Redis主从
🤿 - Redis哨兵(◀️)
🧩 - Redis分片集群
👨💻 - Redis数据结构
♻️ - Redis内存回收
✅ - Redis缓存一致性
主从结构中master节点的作用非常重要,一旦故障就会导致集群不可用。那么有什么办法能保证主从集群的高可用性呢?
Redis提供了哨兵(Sentinel)💂 机制来监控主从集群监控状态,确保集群的高可用性。
哨兵集群作用原理图:

哨兵的作用如下:
💠状态监控 📹:Sentinel 会不断检查您的master和slave是否按预期工作。
💠故障恢复(failover)🔁:如果master故障,Sentinel会将一个slave提升为master。当故障实例恢复后会成为slave。
💠状态通知⁉️:Sentinel充当Redis客户端的服务发现来源,当集群发生failover时,会将最新集群信息推送给Redis的客户端。
那么问题来了,Sentinel怎么知道一个Redis节点是否宕机呢?
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个节点发送ping命令,并通过实例的响应结果来做出判断:
🔹主观下线(sdown):如果某sentinel节点发现某Redis节点未在规定时间响应,则认为该节点主观下线。
🔹 客观下线(odown):若超过指定数量(通过quorum设置)的sentinel都认为该节点主观下线,则该节点客观下线。quorum值最好超过Sentinel节点数量的一半,Sentinel节点数量至少3台。
如图:

一旦发现master故障,sentinel需要在salve中选择一个作为新的master,选择依据是这样的:
🔹 首先会判断slave节点与master节点断开时间长短,如果超过
down-after-milliseconds * 10则会排除该slave节点🔹 然后判断slave节点的
slave-priority值,越小优先级越高,如果是0则永不参与选举(默认都是1)。🔹 如果
slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高🔹 最后是判断slave节点的
run_id大小,越小优先级越高(通过info server可以查看run_id)。
对应的官方文档如下:
https://redis.io/docs/management/sentinel/
💡 问题来了,当选出一个新的master后,该如何实现身份切换呢?
大概分为两步:
🔸在多个
sentinel中选举一个leader🔸由
leader执行failover
首先,Sentinel集群要选出一个执行failover的Sentinel节点,可以成为leader。要成为leader要满足两个条件:
❇️ 最先获得超过半数的投票
❇️ 获得的投票数不小于
quorum值而sentinel投票的原则有两条:
➿ 优先投票给目前得票最多的
➿ 如果目前没有任何节点的票,就投给自己
比如有3个sentinel节点,
s1、s2、s3,假如s2先投票:
✴️ 此时发现没有任何人在投票,那就投给自己。
s2得1票✴️ 接着
s1和s3开始投票,发现目前s2票最多,于是也投给s2,s2得3票
✴️ s2称为leader,开始故障转移
不难看出,谁先投票,谁就会称为leader,那什么时候会触发投票呢?
答案是第一个确认master客观下线的人会立刻发起投票,一定会成为leader。
OK,sentinel找到leader以后,该如何完成failover呢?
我们举个例子,有一个集群,初始状态下7001为master,7002和7003为slave:

假如master发生故障,slave1当选。则故障转移的流程如下:
1)sentinel给备选的slave1节点发送slaveof no one命令,让该节点成为master

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

3)最后,当故障节点恢复后会接收到哨兵信号,执行slaveof 🖥192.168.150.101 7002命令,成为slave:

首先,我们停掉之前的redis集群:
# 老版本DockerCompose docker-compose down # 新版本Docker docker compose down 资料提供的sentinel.conf 📄文件:

其内容如下:
sentinel announce-ip "192.168.150.101" sentinel monitor hmaster 192.168.150.101 7001 2 sentinel down-after-milliseconds hmaster 5000 sentinel failover-timeout hmaster 60000 说明:
🏳️🌈 sentinel announce-ip "192.168.150.101":声明当前sentinel的ip
🏳️🌈 sentinel monitor hmaster 192.168.150.101 7001 2:指定集群的主节点信息
🚩 hmaster:主节点名称,自定义,任意写
🚩 192.168.150.101 7001:主节点的ip和端口
🚩2:认定master下线时的quorum值
🏳️🌈 sentinel down-after-milliseconds hmaster 5000:声明master节点超时多久后被标记下线🏳️🌈 sentinel failover-timeout hmaster 60000:在第一次故障转移失败后多久再次重试我们在虚拟机的/root/redis目录下新建3个文件夹:s1、s2、s3:

将课前资料提供的sentinel.conf文件分别拷贝一份到3个文件夹中。
接着修改docker-compose.yaml文件,内容如下:
version: "3.2" services: r1: image: redis container_name: r1 network_mode: "host" entrypoint: ["redis-server", "--port", "7001"] r2: image: redis container_name: r2 network_mode: "host" entrypoint: ["redis-server", "--port", "7002", "--slaveof", "192.168.150.101", "7001"] r3: image: redis container_name: r3 network_mode: "host" entrypoint: ["redis-server", "--port", "7003", "--slaveof", "192.168.150.101", "7001"] s1: image: redis container_name: s1 volumes: - /root/redis/s1:/etc/redis network_mode: "host" entrypoint: ["redis-sentinel", "/etc/redis/sentinel.conf", "--port", "27001"] s2: image: redis container_name: s2 volumes: - /root/redis/s2:/etc/redis network_mode: "host" entrypoint: ["redis-sentinel", "/etc/redis/sentinel.conf", "--port", "27002"] s3: image: redis container_name: s3 volumes: - /root/redis/s3:/etc/redis network_mode: "host" entrypoint: ["redis-sentinel", "/etc/redis/sentinel.conf", "--port", "27003"] 直接运行命令,启动集群:
docker-compose up -d 运行结果:

我们以s1节点为例,查看其运行日志:
# Sentinel ID is 8e91bd24ea8e5eb2aee38f1cf796dcb26bb88acf # +monitor master hmaster 192.168.150.101 7001 quorum 2 * +slave slave 192.168.150.101:7003 192.168.150.101 7003 @ hmaster 192.168.150.101 7001 * +sentinel sentinel 5bafeb97fc16a82b431c339f67b015a51dad5e4f 192.168.150.101 27002 @ hmaster 192.168.150.101 7001 * +sentinel sentinel 56546568a2f7977da36abd3d2d7324c6c3f06b8d 192.168.150.101 27003 @ hmaster 192.168.150.101 7001 * +slave slave 192.168.150.101:7002 192.168.150.101 7002 @ hmaster 192.168.150.101 7001 可以看到sentinel已经联系到了7001这个节点,并且与其它几个哨兵也建立了链接。哨兵信息如下:
☑️27001:Sentinel ID是8e91bd24ea8e5eb2aee38f1cf796dcb26bb88acf
☑️27002:Sentinel ID是5bafeb97fc16a82b431c339f67b015a51dad5e4f
☑️27003:Sentinel ID是56546568a2f7977da36abd3d2d7324c6c3f06b8d
接下来,我们演示一下当主节点故障时,哨兵是如何完成集群故障恢复(failover)的。
我们连接7001这个master节点,然后通过命令让其休眠60秒,模拟宕机:
# 连接7001这个master节点,通过sleep模拟服务宕机,60秒后自动恢复 docker exec -it r1 redis-cli -p 7001 DEBUG sleep 60 稍微等待一段时间后,会发现sentinel节点触发了failover:

Sentinel的三个作用是什么?
📛集群监控
📛故障恢复
📛状态通知
Sentinel如何判断一个redis实例是否健康?
✅每隔1秒发送一次ping命令,如果超过一定时间没有相向则认为是主观下线(sdown)
✅如果大多数sentinel都认为实例主观下线,则判定服务客观下线(odown)
故障转移步骤有哪些?
✅首先要在sentinel中选出一个leader,由leader执行failover
✅选定一个slave作为新的master,执行slaveof noone,切换到master模式
✅然后让所有节点都执行slaveof 新master
✅ 修改故障节点配置,添加slaveof 新master
sentinel选举leader的依据是什么?
☑️票数超过sentinel节点数量1半
☑️票数超过quorum数量
☑️一般情况下最先发起failover的节点会当选
sentinel从slave中选取master的依据是什么?
⭕首先会判断slave节点与master节点断开时间长短,如果超过down-after-milliseconds * 10则会排除该slave节点
⭕然后判断slave节点的slave-priority值,越小优先级越高,如果是0则永不参与选举(默认都是1)。
⭕如果slave-prority一样,则判断slave节点的offset值,越大说明数据越新,优先级越高
⭕最后是判断slave节点的run_id大小,越小优先级越高(通过info server可以查看run_id)。
分为三步:
1)引入依赖
2)配置哨兵地址
3)配置读写分离
就是SpringDataRedis的依赖:
org.springframework.boot spring-boot-starter-data-redis 连接哨兵集群与传统单点模式不同,不再需要设置每一个redis的地址,而是直接指定哨兵地址:
spring: redis: sentinel: master: hmaster # 集群名 nodes: # 哨兵地址列表 - 192.168.150.101:27001 - 192.168.150.101:27002 - 192.168.150.101:27003 最后,还要配置读写分离,让java客户端将写请求发送到master节点,读请求发送到slave节点。定义一个bean即可:
@Bean public LettuceClientConfigurationBuilderCustomizer clientConfigurationBuilderCustomizer(){ return clientConfigurationBuilder -> clientConfigurationBuilder.readFrom(ReadFrom.REPLICA_PREFERRED); } 这个bean中配置的就是读写策略,包括四种:
MASTER:从主节点读取
MASTER_PREFERRED:优先从master节点读取,master不可用才读取slave
REPLICA:从slave节点读取
REPLICA_PREFERRED:优先从slave节点读取,所有的slave都不可用才读取master