1烂叔、簡(jiǎn)述redis集群的實(shí)現(xiàn)原理
開(kāi)啟cluster功能的redis節(jié)點(diǎn)間愤估,通過(guò)使用ping機(jī)制互通并通告自己的hash slots畅哑,通過(guò)Gossip協(xié)議通告各自所知的其他node的信息旭绒,然后建立連接。
集群中某個(gè)節(jié)點(diǎn)是否失效是通過(guò)集群中超過(guò)半數(shù)節(jié)點(diǎn)的探測(cè)都失效來(lái)決定的京痢,因此為了避免出現(xiàn)腦裂(偶數(shù)節(jié)點(diǎn)之間是否sdown的投票出現(xiàn)均分)奶甘,redis cluster的Master節(jié)點(diǎn)最少數(shù)目是3個(gè)
redis集群支持16384個(gè)hash slot,存儲(chǔ)key時(shí)祭椰,crc16校驗(yàn)算法根據(jù)key值計(jì)算出校驗(yàn)值臭家,然后取模16384,得到的結(jié)果落在0-16383之間的某個(gè)slot上方淤,集群中的Master節(jié)點(diǎn)會(huì)分配0-16383中某一段范圍的slot钉赁,key會(huì)根據(jù)slot存儲(chǔ)在對(duì)應(yīng)的Master節(jié)點(diǎn)上。
由于每個(gè)Master節(jié)點(diǎn)都只存儲(chǔ)一定范圍內(nèi)的slot臣淤,因此存在slot的單點(diǎn)故障橄霉,需要通過(guò)主從復(fù)制實(shí)現(xiàn)Master節(jié)點(diǎn)的高可用;但集群復(fù)制只能支持一層邑蒋,不支持樹(shù)形復(fù)制結(jié)構(gòu),也不支持級(jí)聯(lián)復(fù)制按厘;
客戶端通過(guò)跨集群命令医吊,將key寫入到集群的任意節(jié)點(diǎn),節(jié)點(diǎn)根據(jù)計(jì)算得到key對(duì)應(yīng)的slot逮京,判斷如果slot指向自身卿堂,則直接執(zhí)行命令;否則節(jié)點(diǎn)會(huì)返回客戶端MOVED重定向懒棉,客戶端將命令重定向發(fā)送給目標(biāo)節(jié)點(diǎn)草描。
2、基于redis5的redis cluster部署
按照如下腳本策严,完成1,2項(xiàng)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"關(guān)閉selinux..."$END
setenforce 0
sed -i.bak -r 's|SELINUX=enforcing|SELINUX=disabled|' /etc/selinux/config
systemctl disable --now firewalld && color "firewalld已關(guān)閉" 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 "安裝失敗逛绵,檢查配置參數(shù)" 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 "配置文件復(fù)制完成" 0
chown -R redis.redis ${redis_path} && color "文件權(quán)限修改完畢" 0 || color "文件權(quán)限修改失敗" 1
cat >> /etc/sysctl.conf <<EOF
net.core.somaxconn = 1024
vm.overcommit_memory = 1
EOF
sysctl -p && color "全連接隊(duì)列和內(nèi)存超額分配內(nèi)核參數(shù)修改完成" 0 || color "內(nèi)核參數(shù)修改失敗" 1
echo never > /sys/kernel/mm/transparent_hugepage/enabled && color "關(guān)閉THP(透明大頁(yè))" 0 || "關(guān)閉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 "是否需要將當(dāng)前節(jié)點(diǎn)配置為slave節(jié)點(diǎn)?" askuser1
askuser1=`echo $askuser1 | tr 'A-Z' 'a-z'`
case $askuser1 in
y|yes)
read -p "輸入主節(jié)點(diǎn)IP地址:" slaveip
if [ -z ${slaveip} ];then
while [ -z ${slaveip} ];do
read -p "請(qǐng)輸入主節(jié)點(diǎn)IP地址:" slaveip
done
fi
echo "replicaof ${slaveip} 6379" >> /data/redis/etc/redis.conf && color "redis.conf配置文件修改完畢术浪!" 0
systemctl restart redis@redis && color "服務(wù)重啟完成瓢对!" 0
break
;;
n|no)
break
;;
*)
inputerror
continue
;;
esac
done
}
function sentinel_config {
read -p "輸入主節(jié)點(diǎn)IP地址:" masterip
if [ -z ${masterip} ];then
while [ -z ${masterip} ];do
read -p "請(qǐng)輸入主節(jié)點(diǎn)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 "請(qǐng)輸入希望安裝的redis版本胰苏,回車使用默認(rèn)版本:redis-6.2.6" redis
read -p "請(qǐng)輸入希望安裝的redis路徑硕蛹,回車使用默認(rèn)路徑:/data/redis" predis
redis_version=${redis:-redis-6.2.6}
redis_path=${predis:-/data/redis}
#exec
PS3="請(qǐng)選擇您要執(zhí)行的操作!:"
MENU="
安裝redis
配置redis
配置為從節(jié)點(diǎn)
配置sentinel
退出
"
select M in $MENU ;do
case $REPLY in
1)
redis_install
;;
2)
redis_config
;;
3)
redis_slave
;;
4)
sentinel_config
;;
*)
exit
;;
esac
done
開(kāi)啟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創(chuàng)建cluster
redis-cli --cluster create 192.168.32.187:6379 192.168.32.188:6379 192.168.32.189:6379 <<EOF
yes
EOF
#6臺(tái)機(jī)器的主備部署參考如下配置硕并,前三臺(tái)是master妓美,后三臺(tái)一一對(duì)應(yīng)前三臺(tái)作為slave,同時(shí)hash slot也會(huì)均勻分布到三臺(tái)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
結(jié)果確認(rèn)
[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個(gè)節(jié)點(diǎn)
cluster_size:3 #3臺(tái)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.