1、简述redis集群的实现原理
开启cluster功能的redis节点间,通过使用ping机制互通并通告自己的hash slots,通过Gossip协议通告各自所知的其他node的信息,然后建立连接。
集群中某个节点是否失效是通过集群中超过半数节点的探测都失效来决定的,因此为了避免出现脑裂(偶数节点之间是否sdown的投票出现均分),redis cluster的Master节点最少数目是3个
redis集群支持16384个hash slot,存储key时,crc16校验算法根据key值计算出校验值,然后取模16384,得到的结果落在0-16383之间的某个slot上,集群中的Master节点会分配0-16383中某一段范围的slot,key会根据slot存储在对应的Master节点上。
由于每个Master节点都只存储一定范围内的slot,因此存在slot的单点故障,需要通过主从复制实现Master节点的高可用;但集群复制只能支持一层,不支持树形复制结构,也不支持级联复制;
客户端通过跨集群命令,将key写入到集群的任意节点,节点根据计算得到key对应的slot,判断如果slot指向自身,则直接执行命令;否则节点会返回客户端MOVED重定向,客户端将命令重定向发送给目标节点。
2、基于redis5的redis cluster部署
按照如下脚本,完成1,2项redis的安装和配置:
#!/bin/bash
#****************************************************************************************#
#Author: Yabao11
#QQ: what QQ,no QQ
#Date: 2022-01-04
#FileName: nginx.sh
#URL: https://github.com/yabao11
#Description: Test Script
#Copyright (C): 2022 All rights reserved
#*******************************定义颜色*************************************************#
RED="\e[1;31m"
GREEN="\e[1;32m"
SKYBLUE="\e[1;36m"
YELLOW="\e[1;43m"
BLUE="\e[1;44m"
END="\e[0m"
RandomColor="\e[1;32m"
#****************************************************************************************#
function Ostype {
if grep -i -q "release 6" /etc/centos-release;then
echo Centos6
elif grep -i -q Centos-8 /etc/os-release;then
echo Centos
elif grep -i -q Centos-7 /etc/os-release;then
echo Centos7
elif grep -i -q Ubuntu /etc/os-release;then
echo Ubuntu
elif grep -i -q "RedHat" /etc/os-release;then
echo Redhat
fi
}
function color {
RES_COL=60
MOVE_TO_COL="echo -en \E[${RES_COL}G"
SETCOLOR_SUCCESS="echo -en \E[1;32m"
SETCOLOR_FAILURE="echo -en \E[1;31m"
SETCOLOR_WARNING="echo -en \E[1;33m"
SETCOLOR_NORMAL="echo -en \E[0m"
echo -n "$1" && $MOVE_TO_COL
echo -n "["
if [[ $2 = "success" || $2 = "0" ]]; then
${SETCOLOR_SUCCESS}
echo -n " OK "
elif [[ $2 = "failure" || $2 = "1" ]]; then
${SETCOLOR_FAILURE}
echo -n "FAILED"
else
${SETCOLOR_WARNING}
echo -n "WARNING"
fi
${SETCOLOR_NORMAL}
echo -n "]"
echo
}
function redis_install {
echo -e $GREEN"关闭selinux..."$END
setenforce 0
sed -i.bak -r 's|SELINUX=enforcing|SELINUX=disabled|' /etc/selinux/config
systemctl disable --now firewalld && color "firewalld已关闭" 0
yum -y install gcc jemalloc-devel systemd-devel wget || { color "编译软件安装失败!" 1;exit; }
[ -e /root/${redis_version}.tar.gz ] || wget http://download.redis.io/releases/${redis_version}.tar.gz -P /root/
tar xvf ${redis_version}.tar.gz || { color "文件解压失败" 1;exit; }
cd ${redis_version}
make USE_SYSTEMD=yes PREFIX=${redis_path} install > /dev.null && color "安装成功" 0 || color "安装失败,检查配置参数" 1
echo 'PATH=/data/redis/bin:$PATH' > /etc/profile.d/redis.sh
. /etc/profile.d/redis.sh
id redis || ( useradd -r -s /sbin/nologin redis && color "新增用户redis" 0 || color "用户redis新建失败" 1 )
mkdir /data/redis/{etc,log,data,run}
{ cp redis.conf ${redis_path}/etc/;cp sentinel.conf ${redis_path}/etc/; } && color "配置文件复制完成" 0
chown -R redis.redis ${redis_path} && color "文件权限修改完毕" 0 || color "文件权限修改失败" 1
cat >> /etc/sysctl.conf <<EOF
net.core.somaxconn = 1024
vm.overcommit_memory = 1
EOF
sysctl -p && color "全连接队列和内存超额分配内核参数修改完成" 0 || color "内核参数修改失败" 1
echo never > /sys/kernel/mm/transparent_hugepage/enabled && color "关闭THP(透明大页)" 0 || "关闭THP失败" 1
echo 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' >> /etc/rc.d/rc.local
chmod +x /etc/rc.d/rc.local
[ -e /lib/systemd/system/redis@.service ] && rm -rf /lib/systemd/system/redis@.service
cat > /lib/systemd/system/redis@.service <<EOF
[Unit]
Description=Redis persistent key-value database
After=network.target
[Service]
ExecStart=${redis_path}/bin/redis-server ${redis_path}/etc/%i.conf --supervised systemd
#ExecStop=/usr/libexec/redis-shutdown
ExecStop=/bin/kill -s QUIT \$MAINPID
Type=notify
User=redis
Group=redis
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now redis@redis
}
function redis_config {
sed -i.bak -r -e 's/(^bind\s).*/\10.0.0.0/' \
-e 's/daemonize no/daemonize yes/' \
-e 's|^pidfile.*|pidfile '${redis_path}'/run/redis.pid|' \
-e 's|^logfile.*|logfile '${redis_path}'/log/redis.log|' \
-e 's|^dir.*|dir '${redis_path}'/data|' \
${redis_path}/etc/redis.conf && color "redis.conf配置文件修改完毕!" 0
systemctl restart redis@redis
}
function redis_slave {
while true;do
read -p "是否需要将当前节点配置为slave节点?" askuser1
askuser1=`echo $askuser1 | tr 'A-Z' 'a-z'`
case $askuser1 in
y|yes)
read -p "输入主节点IP地址:" slaveip
if [ -z ${slaveip} ];then
while [ -z ${slaveip} ];do
read -p "请输入主节点IP地址:" slaveip
done
fi
echo "replicaof ${slaveip} 6379" >> /data/redis/etc/redis.conf && color "redis.conf配置文件修改完毕!" 0
systemctl restart redis@redis && color "服务重启完成!" 0
break
;;
n|no)
break
;;
*)
inputerror
continue
;;
esac
done
}
function sentinel_config {
read -p "输入主节点IP地址:" masterip
if [ -z ${masterip} ];then
while [ -z ${masterip} ];do
read -p "请输入主节点IP地址:" masterip
done
fi
sed -i.bak -r -e '/#\s+bind\s+127.0.0.1/ibind 0.0.0.0' \
-e 's|^daemonize.*|daemonize yes|' \
-e 's|^logfile.*|logfile '${redis_path}'/log/redis-sentinel.log|' \
-e 's|^sentinel\smonitor\smymaster.*|sentinel monitor mymaster '${masterip}' 6379 2|' \
-e 's|^sentinel\sdown-after-milliseconds.*|sentinel down-after-milliseconds mymaster 3000|' \
${redis_path}/etc/sentinel.conf && color "sentinel配置文件修改成功!" 0
cat > /lib/systemd/system/redis-sentinel.service <<EOF
[Unit]
Description=Redis Sentinel
After=network.target
[Service]
ExecStart=${redis_path}/bin/redis-sentinel ${redis_path}/etc/sentinel.conf --supervised systemd
ExecStop=/bin/kill -s QUIT \$MAINPID
User=redis
Group=redis
Type=notify
RuntimeDirectory=redis
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl restart redis-sentinel.service
}
read -p "请输入希望安装的redis版本,回车使用默认版本:redis-6.2.6" redis
read -p "请输入希望安装的redis路径,回车使用默认路径:/data/redis" predis
redis_version=${redis:-redis-6.2.6}
redis_path=${predis:-/data/redis}
#exec
PS3="请选择您要执行的操作!:"
MENU="
安装redis
配置redis
配置为从节点
配置sentinel
退出
"
select M in $MENU ;do
case $REPLY in
1)
redis_install
;;
2)
redis_config
;;
3)
redis_slave
;;
4)
sentinel_config
;;
*)
exit
;;
esac
done
开启cluster
sed -i.bak -e 's/bind 127.0.0.1/bind 0.0.0.0/' \
-e '/# cluster-enabled yes/a cluster-enabled yes' \
-e '/# cluster-config-file nodes-6379.conf/a cluster-config-file /data/redis/etc/nodes-6379.conf' \
-e '/cluster-require-full-coverage yes/c cluster-require-full-coverage no' /data/redis/etc/redis.conf
使用redis-cli创建cluster
redis-cli --cluster create 192.168.32.187:6379 192.168.32.188:6379 192.168.32.189:6379 <<EOF
yes
EOF
#6台机器的主备部署参考如下配置,前三台是master,后三台一一对应前三台作为slave,同时hash slot也会均匀分布到三台master
#redis-cli --cluster create 10.0.0.8:6379 10.0.0.18:6379 10.0.0.28:6379 10.0.0.38:6379 10.0.0.48:6379 10.0.0.58:6379 --cluster-replicas 1
结果确认
[root@centos8mini ~]# redis-cli cluster nodes
a9576816037f5f6f103def47473ce8a63c095e9e 192.168.32.188:6379@16379 master - 0 1644374438466 2 connected 5461-10922
df7e0425f345c1bc494c350be88cb1d73acb1932 192.168.32.187:6379@16379 myself,master - 0 1644374434000 1 connected 0-5460
4054278384642524b50caa222820b77fd51f02b0 192.168.32.189:6379@16379 master - 0 1644374437446 3 connected 10923-16383
[root@centos8mini ~]# redis-cli cluster info
cluster_state:ok
cluster_slots_assigned:16384 #分配的hash slot
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:3 #3个节点
cluster_size:3 #3台master
cluster_current_epoch:3
cluster_my_epoch:1
cluster_stats_messages_ping_sent:78
cluster_stats_messages_pong_sent:73
cluster_stats_messages_sent:151
cluster_stats_messages_ping_received:71
cluster_stats_messages_pong_received:78
cluster_stats_messages_meet_received:2
cluster_stats_messages_received:151
[root@centos8mini ~]# redis-cli -c set k1 v1
OK
[root@centos8mini ~]# redis-cli -c set k2 v2
OK
[root@centos8mini ~]# redis-cli -c set k3 v2
OK
[root@centos8mini ~]# redis-cli -c set k3 v3
OK
[root@centos8mini ~]# redis-cli -c get k1
"v1"
[root@centos8mini ~]# redis-cli -c get k2
"v2"
[root@centos8mini ~]# redis-cli -c get k3
"v3"
[root@centos8mini ~]# redis-cli --cluster info 192.168.32.187:6379
192.168.32.187:6379 (df7e0425...) -> 3 keys | 5461 slots | 0 slaves.
192.168.32.188:6379 (a9576816...) -> 1 keys | 5462 slots | 0 slaves.
192.168.32.189:6379 (40542783...) -> 2 keys | 5461 slots | 0 slaves.
[OK] 6 keys in 3 masters.
0.00 keys per slot on average.