前言
前面我们介绍了主从复制集群模式, 但是存在一个问题, redis在主从集群中好像没有共识算法, 比如raft
那么在master挂掉的情况下, 就无法从几个slave节点中自主选举出一个新的 master 节点
redis提供了新的方案, 哨兵机制
本章内容
- 哨兵如何发现redis节点的状态? (哨兵是如何工作的?)
- 如何搭建哨兵集群?
哨兵是如何工作的?
发现问题
哨兵监控redis主从集群各个节点的状态, 以ping pong的方式发送心跳包
Sentinel基于心跳机制监测服务状态,每隔1秒向集群的每个实例发送ping命令:
•主观下线:如果某sentinel节点发现某实例未在规定时间响应,则认为该实例主观下线。(个人认为它下线了)
•客观下线:若超过指定数量(quorum)的sentinel都认为该实例主观下线,则该实例客观下线。quorum值最好超过Sentinel实例数量的一半。(大多数人都认为它下线了)
解决问题
集群故障转移Failover
发现master客观下线, 那么哨兵就会在几个salve集群中选举出一个新的master
Q: 选举条件是什么?
A: 受害者陈某某挂了, 你觉得作为侦探你应该找谁?
条件大概就这些:
- 谁是最后跟陈某某接触的?(数据最新)
- 谁跟陈某某已经就存在矛盾(数据相同, 选slave优先级高的)
- 大家都有嫌疑, 那就找住离陈某某最近的(根据slave的id大小选择, 越小优先级越高)
既然选出了master节点, 那么我们需要怎么切换节点呢?
流程如下:
-
sentinel
给备选的slave1
节点发送slaveof no one
命令,让该节点成为master
-
sentinel
给所有其它slave
发送slaveof 192.168.150.101 7002
命令,让这些slave
成为新master
的从节点,开始从新的master
上同步数据。
- 最后,
sentinel
将故障节点标记为slave
,当故障节点恢复后会自动成为新的master
的slave
节点
搭建哨兵集群
准备
由于哨兵集群有超过一半的投票的确定客观下线的功能, 所以一般哨兵集群为单数, 所以我准备建立三台sentinel哨兵
步骤
- 编写sentinel.conf
- 编写docker-compose.yml
编写sentinel.conf
# sentinel_26379.conf
port 26379
dir /tmp
# 自定义集群名, 其中 192.168.7.2 为 redis 集群 master 主节点的 ip
# 6379 是 master 主节点在本机的端口
# 2 为最小投票数量, 3 台 sentinel 有两台才算投票通过
sentinel monitor mymaster 192.168.7.2 6379 2
# 主节点下线10秒才开始选新节点
sentinel down-after-milliseconds mymaster 10000
# 指定了在发生failover主备切换时最多可以有多少个slave同时对新的master进行同步
# 这个数字越小,完成failover所需的时间就越长,但是如果这个数字越大
# 就意味着越多的slave因为replication而不可用
# 可以通过将这个值设为1来保证每次只有一个slave 处于不能处理命令请求的状态。
sentinel parallel-syncs mymaster 1
sentinel auth-pass mymaster 123456
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
#sentinel_26380.conf
port 26380
dir /tmp
sentinel monitor mymaster 192.168.7.2 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel parallel-syncs mymaster 1
sentinel auth-pass mymaster 123456
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
#sentinel_26381.conf
port 26381
dir /tmp
sentinel monitor mymaster 192.168.7.2 6379 2
sentinel down-after-milliseconds mymaster 10000
sentinel parallel-syncs mymaster 1
sentinel auth-pass mymaster 123456
sentinel failover-timeout mymaster 180000
sentinel deny-scripts-reconfig yes
192.168.7.2
这个ip
, 就是redis
主从集群的master
ip
地址, 端口也是 master
的端口
编写docker-compose.yml
version: '3.9'
services:
redis-sentinel-26379:
image: redis:7.0.9
restart: always
privileged: true
hostname: redis_sentinel_26379
container_name: redis_sentinel_26379
ports:
- 26379:26379
volumes:
- /root/redis_sentinel_cluster/26379/sentinel.conf:/usr/local/etc/redis/sentinel.conf
networks:
redis_cluster:
ipv4_address: 192.168.7.5
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
redis-sentinel-26380:
image: redis:7.0.9
restart: always
privileged: true
hostname: redis_sentinel_26380
container_name: redis_sentinel_26380
ports:
- 26380:26380
volumes:
- /root/redis_sentinel_cluster/26380/sentinel.conf:/usr/local/etc/redis/sentinel.conf
networks:
redis_cluster:
ipv4_address: 192.168.7.6
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
redis-sentinel-26381:
image: redis:7.0.9
restart: always
privileged: true
hostname: redis_sentinel_26381
container_name: redis_sentinel_26381
ports:
- 26381:26381
volumes:
- /root/redis_sentinel_cluster/26381/sentinel.conf:/usr/local/etc/redis/sentinel.conf
networks:
redis_cluster:
ipv4_address: 192.168.7.7
command: redis-sentinel /usr/local/etc/redis/sentinel.conf
networks:
redis_cluster:
name: redis_cluster
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.7.1/24
验证
关闭掉
主节点
然后进入
说明 sentinel 集群生效了
当然我们也可以通过 下面方式查看sentinel状态
root@redis_sentinel_26381:/data# redis-cli -p 26381
127.0.0.1:26381> sentinel master mymaster
1) "name"
2) "mymaster"
3) "ip"
4) "192.168.7.3"
5) "port"
6) "6379"
7) "runid"
8) "6ea1efe878683bfbfe519cf72f5b0967a1883079"
9) "flags"
10) "master"
11) "link-pending-commands"
12) "0"
13) "link-refcount"
14) "1"
15) "last-ping-sent"
16) "0"
17) "last-ok-ping-reply"
18) "716"
19) "last-ping-reply"
20) "716"
21) "down-after-milliseconds"
22) "10000"
23) "info-refresh"
24) "2145"
25) "role-reported"
26) "master"
27) "role-reported-time"
28) "534140"
29) "config-epoch"
30) "1"
31) "num-slaves"
32) "2"
33) "num-other-sentinels"
34) "2"
35) "quorum"
36) "2"
37) "failover-timeout"
38) "180000"
39) "parallel-syncs"
40) "1"
127.0.0.1:26381>
mymaster
就是在sentinel.conf
中配置的名字
总结
哨兵存在的意义是redis主从集群没有共识算法, 或者说没有选举算法, 所以在master挂掉之后, 无法执行选举出一个新的master, 需要sentinel哨兵集群做监控
sentinel哨兵集群使用心跳包的方法, 监听redis主从集群的状态, 通常是每秒1次, ping
发现问题之后, 走故障转移流程, 通过选举出新的master, 然后切换master链接
选举的条件是 数据越新越好, 如果数据的版本一直的话(offset), 那么看哪个slave等级越高越好, 如果等级都一样, 那么看哪个slave的id越小越好
如果优先级为 0 则不参与master选举
从这里看
我们的redis_cluster集群配置文件 docker-compose.yml 需要更改下
version: '3.9'
services:
redis-master-6379:
build: .
restart: always
privileged: true
hostname: redis_6379
container_name: redis_6379
ports:
- 6379:6379
- 16379:16379
volumes:
- /root/redis_cluster/master/6379/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /root/redis_cluster/master/6379/data:/data
networks:
redis_cluster:
ipv4_address: 192.168.7.2
command: redis-server /usr/local/etc/redis/redis.conf
redis-slave-6380:
build: .
restart: always
privileged: true
hostname: redis_6380
container_name: redis_6380
ports:
- 6380:6379
- 16380:16379
volumes:
- /root/redis_cluster/slave/6380/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /root/redis_cluster/slave/6380/data:/data
networks:
redis_cluster:
ipv4_address: 192.168.7.3
command: redis-server /usr/local/etc/redis/redis.conf
redis-slave-6381:
build: .
restart: always
privileged: true
hostname: redis_6381
container_name: redis_6381
ports:
- 6381:6379
- 16381:16379
volumes:
- /root/redis_cluster/slave/6381/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /root/redis_cluster/slave/6381/data:/data
networks:
redis_cluster:
ipv4_address: 192.168.7.4
command: redis-server /usr/local/etc/redis/redis.conf
networks:
redis_cluster:
name: redis_cluster
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.7.1/24
改成
version: '3.9'
services:
redis-6379:
build: .
restart: always
privileged: true
hostname: redis_6379
container_name: redis_6379
ports:
- 6379:6379
- 16379:16379
volumes:
- /root/redis_cluster/master/6379/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /root/redis_cluster/master/6379/data:/data
networks:
redis_cluster:
ipv4_address: 192.168.7.2
command: redis-server /usr/local/etc/redis/redis.conf
redis-6380:
build: .
restart: always
privileged: true
hostname: redis_6380
container_name: redis_6380
ports:
- 6380:6379
- 16380:16379
volumes:
- /root/redis_cluster/slave/6380/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /root/redis_cluster/slave/6380/data:/data
networks:
redis_cluster:
ipv4_address: 192.168.7.3
command: redis-server /usr/local/etc/redis/redis.conf
redis-6381:
build: .
restart: always
privileged: true
hostname: redis_6381
container_name: redis_6381
ports:
- 6381:6379
- 16381:16379
volumes:
- /root/redis_cluster/slave/6381/conf/redis.conf:/usr/local/etc/redis/redis.conf
- /root/redis_cluster/slave/6381/data:/data
networks:
redis_cluster:
ipv4_address: 192.168.7.4
command: redis-server /usr/local/etc/redis/redis.conf
networks:
redis_cluster:
name: redis_cluster
driver: bridge
ipam:
driver: default
config:
- subnet: 192.168.7.1/24
把预先设定的 master 和 slave 容器名字改下