# Redis三主三从集群高可用检查维护文档
# 前言
三主三从是redis cluster最常用的架构,每个从节点复制的都不是本机主库的数据,而是其他节点主库的数据,这样即使某一台主机坏掉了,从节点备份还是在其他机器上,这样就做到了高可用,三主三从架构允许最多坏一台主机。
三台服务器部署时,三主三从我们采用交叉复制架构类型,这样可以做到最多坏一台主机集群还是正常可以用的,如果每台主机的6381节点都是6380节点的备份,那么这台机器坏了,集群就不可用了,因此想要做到高可用,就必须采用交叉复制。如下面架构图所示。
# 系统架构图
三台服务器部署redis集群三主三从(高可用)合理架构图
# 三主三从集群的创建
集群创建时,默认是采用的交叉复制方式,将3个主节点放在3台主机,且每个主节点slave都存放在别的主机。(M:开头为主节点;S:开头为从节点);如下图
三个主节点分别是:10.2.5.121:6380、10.2.5.122:6380、10.2.5.124:6380 三个从节点分别是:10.2.5.121:6381、10.2.5.122:6381、10.2.5.124:6381
# 三主三从集群的停止与启动
步骤1、停止:先停三个从节点,再停三个主节点
步骤2、删除所有主节点、从节点的dump.rdb文件(OA不需要redis持久化数据)
步骤3、启动:先启动三个主节点,再启动三个从节点
重启完成后,请执行检查命令,检查集群状态,确认没有出现2个主节点在同一台主机。
./redis-cli --cluster check 10.2.5.121:6380 -a Seeyon123456
# 集群高可用注意事项
集群高可用因避免出现2个主节点再同一台服务器。 可能出现的情况如下:
1、其中一台主机发生故障,主机上的2个redis节点宕机,集群主从做了切换。
模拟10.2.5.121服务器宕机,此时查询redis集群状态如下
可以看出2个主节点:10.2.5.122:6380、10.2.5.122:6381,在同一台服务器上了。此时,再启动121上的2个redis服务,就是变成从节点。
2、手动停过redis服务
如果出现2个主节点再同一台服务器,我们需要把关系切换回来,不能让一台机器上同时存在两台主库,每次故障处理后一定要把主从架构修改会原来的样子。
解决策略:将原来的主库(新从库)重新变为主库
已下图为例:10.2.5.122服务器出现了2个主节点。在集群创建时,10.2.5.122:6381原本是从节点,现在变成了主节点,它对应的从节点10.2.5.121:6380原本是主节点,现在变成了从节点,原因就是2者之间做了主从切换
解决方法一:在122服务器上停掉主节点M2,等121上的从节点S2切换成主后,在重新启动
解决方法二:将10.2.5.121:6380从库切换成主库,只需要连上10.2.5.121:6380,执行CLUSTER FAILOVER即可变为主库
将新从库重新切换为主库
[root@redis-1 ~]# redis-cli -h 10.2.5.121 -p 6380 -a Seeyon123456
10.2.5.121:6380> CLUSTER FAILOVER
OK
# 附言
主从数据同步原理:
1、全量同步
当一个redis服务器初次向主服务器发送salveof命令时,redis从服务器会进行一次全量同步。
salveof:将当前服务器转变为指定服务器的从属服务器,就是给主机加一个从机
1、 slave服务器向master发送psync命令,告诉master我需要同步数据。
2、 master接收到psync命令后会进行BGSAVE命令生成RDB文件快照。
3、生成完后,会将RDB文件发送给slave。
4、slave接收到文件会载入RDB快照,并且将数据库状态变更为master在执行BGSAVE时的状态一致。
5、 master会发送保存在缓冲区里的所有写命令,告诉slave可以进行同步了
6、slave执行这些写命令。
2、增量同步
slave已经同步过master了,那么如果后续master进行了写操作,比如说一个简单的set name redis,那么master执行过当前命令后,会将当前命令发送给slave执行一遍,达成数据一致性。
3、重新复制
当slave断开重连之后会进行重新同步,重新同步分完全同步和部分同步。
1、当slave断开重连后,会发送psync 命令给master。
2、master收到psync后会返回+continue回复,表示slave可以执行部分同步了。
3、master发送断线后的写命令给slave
4、slave执行写命令。
# redis三主三从集群检查脚本
#!/bin/bash
function echo_color_green (){
echo -e "\033[1;32m$1\033[0m"
}
function echo_color_red (){
echo -e "\033[1;31m$1\033[0m"
}
function echo_color_yellow (){
echo -e "\033[1;33m$1\033[0m"
}
function echo_color_blue (){
echo -e "\033[1;34m$1\033[0m"
}
redis_cli=`find ./ -name 'redis-cli'|head -n 1`
redis_conf=`find ./ -name 'redis.conf'|head -n 1`
host=`less $redis_conf|grep '^bind'| awk '{print $2}'`
port=`less $redis_conf|grep '^port'| awk '{print $2}'`
password=`less $redis_conf|grep '^requirepass'|awk '{print $2}'`
master1_id=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep master|awk '{print $1}'|sed -n 1p`
master2_id=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep master|awk '{print $1}'|sed -n 2p`
master3_id=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep master|awk '{print $1}'|sed -n 3p`
master1_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep master|awk '{print $2}'|awk -F':' '{print $1}'|sed -n 1p`
master2_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep master|awk '{print $2}'|awk -F':' '{print $1}'|sed -n 2p`
master3_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep master|awk '{print $2}'|awk -F':' '{print $1}'|sed -n 3p`
master1_slave_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep slave| grep $master1_id |awk '{print $2}'|awk -F':' '{print $1}'`
master2_slave_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep slave| grep $master2_id |awk '{print $2}'|awk -F':' '{print $1}'`
master3_slave_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep slave| grep $master3_id |awk '{print $2}'|awk -F':' '{print $1}'`
if [ $master1_host == $master2_host ];then
if [ $master1_slave_host == $master3_slave_host ];then
err_master=$master1_id
fi
if [ $master2_slave_host == $master3_slave_host ];then
err_master=$master2_id
fi
elif [ $master1_host == $master3_host ];then
if [ $master1_slave_host == $master2_slave_host ];then
err_master=$master1_id
fi
if [ $master2_slave_host == $master3_slave_host ];then
err_master=$master3_id
fi
elif [ $master2_host == $master3_host ];then
if [ $master1_slave_host == $master3_slave_host ];then
err_master=$master3_id
fi
if [ $master1_slave_host == $master2_slave_host ];then
err_master=$master2_id
fi
fi
if [ $err_master ];then
err_slave_host=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep slave|grep $err_master|awk '{print $2}'|awk -F':' '{print $1}'`
err_slave_port=`$redis_cli -c -h $host -p $port -a $password cluster nodes 2>/dev/null |grep slave|grep $err_master|awk -F':' '{print $2}'|awk -F'@' '{print $1}'`
echo_color_red "Redis endpoint $err_slave_host:$err_slave_port need to execute 'CLUSTER FAILOVER'."
echo_color_blue "Command:"
echo_color_green " $redis_cli -c -h $err_slave_host -p $err_slave_port -a $password CLUSTER FAILOVER"
read -p "是否执行命令(Y/N):" choice
case $choice in
y|Y)
$redis_cli -c -h $err_slave_host -p $err_slave_port -a $password CLUSTER FAILOVER 2>/dev/null
sleep 5
echo_color_green "Execute finished.\n"
$redis_cli --cluster check $host:$port -a $password 2>/dev/null;;
n|N)
echo_color_blue "Exit."
exit;;
*)
echo_color_yellow "Input Eror, Exit."
exit;;
esac
else
echo_color_green "Cluster is OK."
fi
