Redis 哨兵模式核心原理
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis 哨兵模式核心原理相关的知识,希望对你有一定的参考价值。
参考技术Asentinal,中文名是哨兵
哨兵是redis集群架构中非常重要的一个组件,主要功能如下:
(1)集群监控,负责监控redis master和slave进程是否正常工作
(2)消息通知,如果某个redis实例有故障,那么哨兵负责发送消息作为报警通知给管理员
(3)故障转移,如果master node挂掉了,会自动转移到slave node上
(4)配置中心,如果故障转移发生了,通知client客户端新的master地址
哨兵本身也是分布式的,作为一个哨兵集群去运行,互相协同工作
(1)故障转移时,判断一个master node是宕机了,需要大部分的哨兵都同意才行,涉及到了分布式选举的问题
(2)即使部分哨兵节点挂掉了,哨兵集群还是能正常工作的,因为如果一个作为高可用机制重要组成部分的故障转移系统本身是单点的,这样就无法做到高可用了。
(1)哨兵至少需要3个实例,来保证自己的健壮性
(2)哨兵 + redis主从的部署架构,是不会保证数据零丢失的,只能保证redis集群的高可用性
(3)对于哨兵 + redis主从这种复杂的部署架构,尽量在测试环境和生产环境,都进行充足的测试和演练
1 哨兵集群必须部署2个以上节点
如果哨兵集群仅仅部署了个2个哨兵实例,quorum=1
Configuration: quorum = 1
master宕机,s1和s2中只要有1个哨兵认为master宕机就可以还行切换,同时s1和s2中会选举出一个哨兵来执行故障转移。
同时这个时候,需要majority,也就是大多数哨兵都是运行的,2个哨兵的majority就是2(2的majority=2,3的majority=2,5的majority=3,4的majority=2),2个哨兵都运行着,就可以允许执行故障转移。
但是如果整个M1和S1运行的机器宕机了,那么哨兵只有1个了,此时就没有majority来允许执行故障转移,虽然另外一台机器还有一个R1,但是故障转移不会执行。
2 经典的哨兵集群
Configuration: quorum = 2,majority
如果M1所在机器宕机了,那么三个哨兵还剩下2个,S2和S3可以一致认为master宕机,然后选举出一个来执行故障转移。
同时3个哨兵的majority是2,所以还剩下的2个哨兵运行着,就可以允许执行故障转移。
sdown和odown两种失败状态
sdown是主观宕机,就一个哨兵如果自己觉得一个master宕机了,那么就是主观宕机。
odown是客观宕机,如果quorum数量的哨兵都觉得一个master宕机了,那么就是客观宕机。
sdown达成的条件很简单,如果一个哨兵ping一个master,超过了is-master-down-after-milliseconds指定的毫秒数之后,就主观认为master宕机。
sdown到odown转换的条件很简单,如果一个哨兵在指定时间内,收到了quorum指定数量的其他哨兵也认为那个master是sdown了,那么就认为是odown了,客观认为master宕机。
哨兵互相之间的发现,是通过redis的pub/sub系统实现的,每个哨兵都会往 sentinel :hello这个channel里发送一个消息,这时候所有其他哨兵都可以消费到这个消息,并感知到其他的哨兵的存在
每隔两秒钟,每个哨兵都会往自己监控的某个master+slaves对应的 sentinel :hello channel里发送一个消息,内容是自己的host、ip和runid还有对这个master的监控配置。
每个哨兵也会去监听自己监控的每个master+slaves对应的 sentinel :hello channel,然后去感知到同样在监听这个master+slaves的其他哨兵的存在。
每个哨兵还会跟其他哨兵交换对master的监控配置,互相进行监控配置的同步。
哨兵会负责自动纠正slave的一些配置,比如slave如果要成为潜在的master候选人,哨兵会确保slave在复制现有master的数据; 如果slave连接到了一个错误的master上,比如故障转移之后,那么哨兵会确保它们连接到正确的master上
如果一个master被认为odown了,而且majority哨兵都允许了主备切换,那么某个哨兵就会执行主备切换操作,此时首先要选举一个slave来
会考虑slave的一些信息
(1)跟master断开连接的时长
(2)slave优先级
(3)复制offset
(4)run id
如果一个slave跟master断开连接已经超过了down-after-milliseconds的10倍,外加master宕机的时长,那么slave就被认为不适合选举为master。
(down-after-milliseconds * 10) + milliseconds_since_master_is_in_SDOWN_state
接下来会对slave进行排序
(1)按照slave优先级进行排序,slave priority越低,优先级就越高
(2)如果slave priority相同,那么看replica offset,哪个slave复制了越多的数据,offset越靠后,优先级就越高
(3)如果上面两个条件都相同,那么选择一个run id比较小的那个slave
每次一个哨兵要做主备切换,首先需要quorum数量的哨兵认为odown,然后选举出一个哨兵来做切换,这个哨兵还得得到majority哨兵的授权,才能正式执行切换
如果quorum < majority,比如5个哨兵,majority就是3,quorum设置为2,那么就3个哨兵授权就可以执行切换
但是如果quorum >= majority,那么必须quorum数量的哨兵都授权,比如5个哨兵,quorum是5,那么必须5个哨兵都同意授权,才能执行切换
哨兵会对一套redis master+slave进行监控,有相应的监控的配置
执行切换的那个哨兵,会从要切换到的新master(salve->master)那里得到一个configuration epoch,这就是一个version号,每次切换的version号都必须是唯一的
如果第一个选举出的哨兵切换失败了,那么其他哨兵,会等待failover-timeout时间,然后接替继续执行切换,此时会重新获取一个新的configuration epoch,作为新的version号
哨兵完成切换之后,会在自己本地更新生成最新的master配置,然后同步给其他的哨兵,就是通过之前说的pub/sub消息机制
这里之前的version号就很重要了,因为各种消息都是通过一个channel去发布和监听的,所以一个哨兵完成一次新的切换之后,新的master配置是跟着新的version号的
其他的哨兵都是根据版本号的大小来更新自己的master配置的
redis进阶:哨兵模式工作原理及搭建
0. 引言
上一章我们讲解了redis的主从搭建,但要实现真正的可靠的主从结构,还需要实现主从切换。也就是当主节点宕机时,从节点能够自动切换为主节点。
这就需要借助哨兵模式来实现,今天我们就来看如何搭建哨兵模式
1. 原理
1.1 哨兵的作用
所谓哨兵就是有一个放哨的,一旦发现主节点不行了,就会告诉从节点顶上。
它的核心作用有三个:
- 监控节点状态,判断主节点是否挂掉
- 当主节点挂掉时,进行主从切换,选出新的主节点
- 将选出新主的消息告诉其他节点以及客户端
所以它的原理其实就围绕这三点展开
1.2 监控节点状态
哨兵会定时发送三个任务:
(1)每10s会向主节点发送info
指令,并通过主节点来获取配置的从节点信息,也就是主从节点的网络拓扑图(结构信息)。这样当有新的从节点配置进来时可以感知到
(2)每2s广播当前哨兵对于主节点的判断(是否主观下线)以及当前哨兵节点的信息(让别的哨兵知道“我”还活着)。这里广播是通过redis的发布订阅模式来实现的。
(3)每1s会向主从节点、其他哨兵发送ping命令,也就是心跳检测,检查这些节点是否还活着。
1、从第三个任务我们可以看到,哨兵每1s会发送一个ping
命令给监控的主从节点,在规定时间内(is-master-down-after-milliseconds
)没有收到有效回复时就会认为这个节点下线
了。
但是这里会涉及一个问题,生活中我们一般认为一个人的想法是主观想法,大多数人的想法近似于客观想法。redis中也有这样一个概念,一个哨兵节点认为某个节点挂了,这是主观下线;大多数哨兵节点认为某个节点挂了,这是客观下线。这样的好处也很明显,可以减少误判。
比如说某个哨兵节点的网络刚好有波动,ping节点时没有ping通,结果就认为redis节点挂了,实际上是网络的问题。而大多数哨兵都同时出现网络问题的概念就比较低了,触发机房整体出现网络问题,那这是redis节点也自然挂了。
2、所以当一个哨兵节点认为这个主节点主观下线后,就会发送命令is-master-down-by-addr
询问其他哨兵,该节点有没有下线。
3、如果达到指定数量(quorum
,默认半数以上)的哨兵节点都认为某个主节点主观下线了, 该哨兵就会认为该主节点客观下线
了。
通过这样来监控节点的状态
1.3 哨兵的的个数
当哨兵判定某个节点为客观下线
后,就会向其他哨兵发起投票,选出一个leader哨兵,最后由这个leader哨兵来进行主从切换
所以我们一般要求哨兵节点为2个及以上,且为奇数,也就是至少3个。想想为什么?
因为如果为偶数个的哨兵节点时,就会出现"脑裂"问题,这个在es集群中也比较常见,比如一半的哨兵投了反对票, 一半的投了支持票,请问最终结果如何?
其次因为哨兵需要半数以上投票通过才能通过,当为2节点时。如果宕机了一个节点,就永远只有一个节点,则永远不能超过半数以上了,而为2节点时,即使宕机1个,还有2个节点可以投票,也能满足半数以上。
所以,我们必然需要奇数个哨兵才能真正投票出结果
1.4 哨兵的投票机制
但奇数个哨兵就万事大吉了吗?如果每个哨兵都投个自己,那不是没法选出一个leader了吗?
redis哨兵的投票算法当然想到了这点。
1、首先,最先发现主节点为客观下线的哨兵,redis将其定义为“候选者”
2、候选者会先投自己一票,可以理解为“我发现的问题,我要自己解决”
3、然后会向其他哨兵发起投票请求(请把票投给我),每个哨兵都有一票,候选者可以把票投给自己,非候选者收到投票请求后,可以选择同意或拒绝,如果收到两个候选者的投票请求,先到先得。
4、候选者会统计自己获取到的票数,直到获取的票数超过半数,且超过所配置的quorum
数,这次选举就成功了,会晋升为Leader,并且负责主从切换事宜。
1.5 哨兵主从切换
当选举出哨兵Leader后,就可以进行主从切换了。
1、从挂掉的主节点下的所有的从节点中选一个状态健康、优先级高且数据完整的从节点。
所谓状态健康就是网络连接正常的,主要是主从节点在
down-after-milliseconds
时间内产生过网络通信的即可认为是健康的从节点。而优先级高则是根据
slave-priority
配置的值来判定的数据完整则是当优先级相同,复制的数据下标越多则数据越完整(主从复制时从节点会记录复制的数据下标),如果优先级和数据完整都想同时就会选择节点ID最小的那个
这里大家可能会想:“哨兵是怎么知道主节点下有哪些从节点的,主节点不是都挂掉了吗?”
答:还记得我们上面讲的哨兵的三个定时任务吗?哨兵就是通过第一个定时任务获取的从节点信息,并且记录在册,以供使用。
2、然后通过指令slaveof no one
清除这个从节点的SLAVE信息,也就是取消掉其附属的主节点,让其成为主节点。
3、同时将挂掉的主节点的所有从节点信息再挂载到新的主节点上
4、至此,内部的主从转换就完成了,但下一步我们还需要将新主节点的信息广播给客户端。当然这里的广播是通过redis的发布/订阅模式来实现的
5、还没完,旧的主节点哨兵也会持续监听,当它恢复后,哨兵会将其作为从节点挂载到新主节点上。所以这里要注意,主节点宕机后再恢复,并不会自动再切回主节点,而是作为从节点存在。
至此,我们关于哨兵模式的原理就介绍完成了。下面我们来看具体实操。
2. 哨兵搭建
1、首先准备三个节点,一主双从,并启动。如果不知道如何搭建的,可以参考我之前的文章:
安装redis的四种方式
redis进阶:搭建redis主从架构
2、可以单独启动redis节点作为哨兵节点,也可以配置到原有的数据节点(主从节点)中,这里为了节约服务器资源,我们将哨兵配置到原来的主从节点中
3、哨兵有一个单独的配置文件sentinel.conf
,在redis的安装目录下可以看到
4、修改三个节点的配置文件
vim sentinel.conf
修改内容:
# 关闭保护模式
protected-mode no
# 哨兵监听端口,默认26379
port 26379
# 后台启动,即开启守护进程
daemonize yes
# 日志路径
logfile "/var/local/redis/logs/sentinel.log"
# 指定数据存放路径,默认/tmp
dir "/tmp"
# 监听的主节点 mymaster为主节点名称,最后的2为quorum值
sentinel monitor mymaster 192.168.244.27 6379 2
# 如果master节点有密钥要补充密码
#sentinel auth-pass mymaster xxx
# 判定服务器down掉的时间周期,默认30000毫秒(30秒)
sentinel down-after-milliseconds mymaster 30000
# 故障节点的最大超时时间为180000(180秒)
sentinel failover-timeout mymaster 180000
5、如果未关闭防火墙,需要开启哨兵监听端口
# 查询端口是否开放
firewall-cmd --query-port=26379/tcp
# 开启端口
firewall-cmd --add-port=26379/tcp --permanent
# 开启后重新加载
firewall-cmd --reload
6、启动哨兵
cd redis安装目录
redis-sentinel sentinel.conf
7、查看日志
tail -f /var/local/redis/logs/sentinel.log
8、查看哨兵是否启动
(1)通过ps指令查询
ps -ef | grep sentinel
(2)通过redis-cli连接哨兵节点,连接成功说明启动成功
3. 测试
1、关闭主节点
(1)查询进程
ps -ef | grep reids
(2)kill 主节点
kill -9 1212
2、查看哨兵日志
可以看到已经切换到从节点28服务器了
3、查看从节点信息,到新主节点上执行
redis-cli info replication
从节点已经更新为主节点了,并且其下现在只有一个从节点
4、我们再把原主节点启动起来
service redis start
# 我这里已经将启动脚本配置到/etc/init.d文件夹并命名为redis了,所以可以通过service启动,如果没有配置的,使用以下指令启动
redis-server /etc/redis/6379.conf
5、观察哨兵日志,发现原主节点被转换为从节点
6、再次观察从节点信息
新增了一个从节点,即原来的主节点,与我们上述的原理解析相符
7、我们在尝试在新节点上更新一个key
8、从节点查看该key
主从切换成功,哨兵搭建完成
4. 配置哨兵开机启动
1、之前我们已经讲解了设置redis开机自启,我们在此基础上操作
2、修改原有redis启动脚本
vim /etc/init.d/redis
脚本内容
REDISPORT=6379
# 配置哨兵端口
REDIS_SENTINEL=26379
EXEC=/var/local/redis/redis-6.2.7/src/redis-server
CLIEXEC=/var/local/redis/redis-6.2.7/src/redis-cli
# 配置哨兵执行脚本
EXEC_SENTINEL=/var/local/redis/redis-6.2.7/src/redis-sentinel
PIDFILE=/var/run/redis_$REDISPORT.pid
CONF="/etc/redis/$REDISPORT.conf"
# 哨兵配置文件路径
SENTINEL_CONF="/var/local/redis/redis-6.2.7/sentinel.conf"
case "$1" in
start)
if [ -f $PIDFILE ]
then
echo "$PIDFILE exists, process is already running or crashed"
else
echo "Starting Redis server..."
$EXEC $CONF
# 启动哨兵服务
echo "Starting Redis-Sentinel server..."
$EXEC_SENTINEL $SENTINEL_CONF
fi
;;
stop)
if [ ! -f $PIDFILE ]
then
echo "$PIDFILE does not exist, process is not running"
else
PID=$(cat $PIDFILE)
echo "Stopping ..."
$CLIEXEC -p $REDISPORT shutdown
# 关闭哨兵服务
echo "Stoping sentinel..."
$CLIEXEC -p $REDIS_SENTINEL shutdown
while [ -x /proc/$PID ]
do
echo "Waiting for Redis to shutdown ..."
sleep 1
done
echo "Redis stopped"
fi
;;
*)
echo "Please use start or stop as first argument"
;;
esac
3、如果之前没配置过,则需要操作以下步骤,如果已经按照过我之前的配置添加过redis开机自启的则无需配置
(1)赋予文本权限
chmod 777 /etc/init.d/redis
(2)添加开机自启
chkconfig redis on
4、重启哨兵服务器,连接哨兵端口测试
redis-cli -p 26379
连接成功,说明启动成功
总结
至此,我们哨兵模式的搭建就完成了,希望大家可以借此更加深入理解redis原理,下一期,我们继续讲解redis的集群模式,实现真正的redis高可用
以上是关于Redis 哨兵模式核心原理的主要内容,如果未能解决你的问题,请参考以下文章