Redis 主從復(fù)制、哨兵模式、集群
點(diǎn)擊上方“程序員大白”,選擇“星標(biāo)”公眾號
重磅干貨,第一時間送達(dá)
1、單機(jī)模式
持久化是最簡單的高可用方法(有時甚至不被歸為高可用的手段),主要作用是數(shù)據(jù)備份,即將數(shù)據(jù)存儲在硬盤,保證數(shù)據(jù)不會因進(jìn)程退出而丟失。
缺點(diǎn)
單機(jī)故障,無法保證數(shù)據(jù)的安全
讀寫操作無法負(fù)載均衡
容量瓶頸,存儲能力受到限制
2、主從復(fù)制
復(fù)制是高可用 Redis 的基礎(chǔ),哨兵和集群都是在復(fù)制基礎(chǔ)上實(shí)現(xiàn)高可用的。復(fù)制主要實(shí)現(xiàn)了數(shù)據(jù)的多機(jī)備份(容災(zāi)備份),以及對于讀操作的負(fù)載均衡(寫操作仍然在主機(jī))和簡單的故障恢復(fù)。
master:寫操作
slave:讀操作(負(fù)載均衡),不能寫

配置
公共配置(端口,文件保存路徑,根據(jù)環(huán)境配置)
bind 0.0.0.0
port 6000
daemonize yes
pidfile "/home/eric/redis-masterslave/redis6000/redis_6000.pid"
logfile "/home/eric/redis-masterslave/redis6000/redis.log"
dbfilename "dump.rdb"
dir "/home/eric/redis-masterslave/redis6000"
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb配置主機(jī)地址與密碼(配從不配主)
masterauth:主要是針對 master 對應(yīng)的 slave 節(jié)點(diǎn)設(shè)置的,在 slave 節(jié)點(diǎn)數(shù)據(jù)同步的時候用到。
requirepass:對登錄權(quán)限做限制,redis 每個節(jié)點(diǎn)的 requirepass 可以是獨(dú)立、不同的。
replicaof 127.0.0.1 6000
masterauth 123456 # 如果主機(jī)設(shè)置密碼的話,需要填寫(主機(jī)使用 requirepass, 從機(jī)使用 masterauth)
查看節(jié)點(diǎn)信息
127.0.0.1:6000> info replication
配置建議
主機(jī)通常不配置 RDB 和 AOF 持久化
save ""
appendonly no
工作流程

建立連接
設(shè)置 master 的地址和端口,發(fā)送 slaveof ip port 指令,master 會返回響應(yīng)客戶端,根據(jù)響應(yīng)信息保存 master 的 run id 和 ip port 信息(連接測試)
根據(jù)保存的信息創(chuàng)建連接 master 的 socket
周期性發(fā)送 ping,master 會相應(yīng) pong
發(fā)送指令 auth password(身份驗(yàn)證),master 驗(yàn)證身份
發(fā)送 slave 端口信息,master 保存 salve 端口號
數(shù)據(jù)同步

slave 發(fā)送指令 psync2(psync2 run_id -1)
主機(jī)返回當(dāng)前的 runid 和 offset 給 salve
salve 保存主機(jī)的相關(guān)信息
master 執(zhí)行 bgsave
在第一個 salve 連接時,創(chuàng)建命令緩存區(qū)
生產(chǎn) RDB 文件,通過 socket 發(fā)送給 slave
slave 接收 RDB 文件,清空數(shù)據(jù),執(zhí)行 RDB 文件恢復(fù)過程
發(fā)送命令告知 RDB 恢復(fù)已經(jīng)完成(告知全量復(fù)制完成)
master 發(fā)送復(fù)制緩沖區(qū)信息
slave 接收信息,執(zhí)行重寫后恢復(fù)數(shù)據(jù)
注意 master 會保存 slave 從我這里拿走了多少數(shù)據(jù),保存 slave 的偏移量
全量復(fù)制消耗
bgsave 時間
RDB 文件網(wǎng)絡(luò)傳輸
從節(jié)點(diǎn)請求數(shù)據(jù)時間
從節(jié)點(diǎn)加載 RDB 的時間
可能的 AOF 重寫時間
# 查看 runid
127.0.0.1:6001> info server
# Server
...
run_id:ac11dde108fa5288af0f7d72c6c0f20e4611157d
...
# 查看 offset
127.0.0.1:6002> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=127.0.0.1,port=6000,state=online,offset=3482,lag=1
slave1:ip=127.0.0.1,port=6001,state=online,offset=3482,lag=1
master_repl_offset:3482
...
命令傳播
slave 心跳:replconf ack (offset)匯報(bào) slave 自己的 offset,獲取最新的數(shù)據(jù)指令
命令傳播階段出現(xiàn)斷網(wǎng):
網(wǎng)絡(luò)閃斷、閃連:忽略
短時間斷網(wǎng):增量
長時間斷網(wǎng):全量
全量復(fù)制核心三個要素
服務(wù)器運(yùn)行ID
用于服務(wù)器之間通信驗(yàn)證身份,master 首次連接 slave 時,會將自己的 run id 發(fā)送給 slave,slave 保存此 ID
主服務(wù)器積壓的命令緩沖區(qū)
先進(jìn)先出隊(duì)列
主服務(wù)器的復(fù)制偏移量
用于比對偏移量,然后判斷出執(zhí)行全量還是增量
切換主從命令
將當(dāng)前服務(wù)器轉(zhuǎn)變?yōu)橹付ǚ?wù)器的從屬服務(wù)器
slaveof 127.0.0.1 6000
將從屬服務(wù)器用作新的主服務(wù)器
slaveof no noe
缺點(diǎn)
存儲能力受到單機(jī)的限制
缺陷是故障恢復(fù)無法自動化
寫操作無法負(fù)載均衡
3、哨兵模式
在 Redis 2.8 版本開始引入,在主從復(fù)制的基礎(chǔ)上,哨兵實(shí)現(xiàn)了自動化的故障恢復(fù)。通俗的來說哨兵模式的出現(xiàn)時為了解決主從復(fù)制模式中需要人為操作的東西,變?yōu)樽詣硬僮?/span>

功能
監(jiān)控(Monitoring):哨兵會不斷的檢查主節(jié)點(diǎn)和從節(jié)點(diǎn)是否正常工作
自動故障轉(zhuǎn)移(Automatic Failover):當(dāng)主節(jié)點(diǎn)不能正常工作時,哨兵會開始自動故障轉(zhuǎn)移操作,它會選擇失效的主節(jié)點(diǎn)里其中一個從節(jié)點(diǎn)升級為新的主節(jié)點(diǎn),并讓其他從節(jié)點(diǎn)改為復(fù)制新的主節(jié)點(diǎn)中的數(shù)據(jù)
配置提供者(Configuration Provider):客戶端在初始化時,通過連接哨兵來獲取當(dāng)前 Redis 服務(wù)的地址
通知(Notification):哨兵可以將故障轉(zhuǎn)移的結(jié)果發(fā)送給客戶端
其中,監(jiān)控和自動故障轉(zhuǎn)移功能,使得哨兵可以及時發(fā)現(xiàn)主節(jié)點(diǎn)的故障并完成轉(zhuǎn)移,而配置提供者和通知功能,則需要在與客戶端交互中才能體現(xiàn)
架構(gòu)
哨兵節(jié)點(diǎn):哨兵系統(tǒng)由一個或多個哨兵節(jié)點(diǎn)組成,哨兵節(jié)點(diǎn)是特殊的 Redis 節(jié)點(diǎn),不存儲數(shù)據(jù)
數(shù)據(jù)節(jié)點(diǎn):主節(jié)點(diǎn)和從節(jié)點(diǎn)都是數(shù)據(jù)節(jié)點(diǎn)
配置
配置哨兵
復(fù)制 sentinel.conf 至目標(biāo)目錄下,并進(jìn)行修改
bind 0.0.0.0
port 8000
daemonize yes
pidfile /home/eric/redis-sentinel/redis8000/redis-sentinel-8000.pid
logfile "/home/eric/redis-sentinel/redis8000/sentinel-8000.log"
dir /home/eric/redis-sentinel/redis8000
sentinel monitor mymaster 127.0.0.1 7000 2
# sentinel auth-pass <master-name> <password>
其中 sentinel monitor mymaster 127.0.0.1 7000 2 的含義是:該節(jié)點(diǎn)監(jiān)控 127.0.0.1 7000 這個主節(jié)點(diǎn),該主節(jié)點(diǎn)的名稱定義為 mymaster,最后的 2 的含義與主節(jié)點(diǎn)故障判定有關(guān),即至少需要 2 個哨兵同意(認(rèn)為該主機(jī)點(diǎn)已經(jīng)不能提供服務(wù)),才能判斷該主節(jié)點(diǎn)發(fā)生故障并進(jìn)行故障轉(zhuǎn)移。
配置數(shù)據(jù)(主從)節(jié)點(diǎn)
同主從配置
啟動
啟動數(shù)據(jù)(主從)節(jié)點(diǎn)
哨兵系統(tǒng)的主從節(jié)點(diǎn)與普通的主從節(jié)點(diǎn)配置完全相同,并不需要做額外的處理
replicaof <masterip> <masterport>
啟動哨兵
redis-sentinel ./redis8000/sentinel.conf
redis-sentinel ./redis8001/sentinel.conf
redis-sentinel ./redis8002/sentinel.conf
當(dāng)發(fā)送故障轉(zhuǎn)移的時候,哨兵會自己修改配置文件中的 sentinel monitor 配置
客戶端(Jedis)訪問哨兵系統(tǒng)
Set<String> set = new HashSet<>();
set.add("127.0.0.1:8000");
set.add("127.0.0.1:8001");
set.add("127.0.0.1:8002");
JedisSentinelPool jedisSentinelPool= new JedisSentinelPool("mymaster", set, "pwd");
Jedis jedis = jedisSentinelPool.getResource();
jedis.set("key", "value");
原理
相關(guān)概念
主觀下線:在心跳檢測的定時任務(wù)中,如果其他節(jié)點(diǎn)超過一定的時間沒有響應(yīng),哨兵節(jié)點(diǎn)就會將其進(jìn)行主觀下線,顧名思義,主觀下線是一個哨兵節(jié)點(diǎn)“主觀的”判斷下線,與其對應(yīng)的是客觀下線
客觀下線:哨兵節(jié)點(diǎn)在對主節(jié)點(diǎn)進(jìn)行主觀下線后,會通過 sentinel is-maste-down-by-addr 命令詢問其他哨兵節(jié)點(diǎn)該主節(jié)點(diǎn)的狀態(tài),如果判斷主節(jié)點(diǎn)下線的哨兵達(dá)到一定數(shù)量后,則對該主節(jié)點(diǎn)進(jìn)行客觀下線
需要注意的是:客觀下線是主節(jié)點(diǎn)才有的概念,如果哨兵發(fā)現(xiàn)從節(jié)點(diǎn)故障,被哨兵主觀下線后,不會再有后續(xù)的客觀下線和故障轉(zhuǎn)移操作。
定時任務(wù):每個哨兵節(jié)點(diǎn)維護(hù)了 3 個定時任務(wù),定時任務(wù)的功能分別如下:
每 10 秒通過向該主節(jié)點(diǎn)發(fā)送 info 命令獲取最新的主從結(jié)構(gòu)
發(fā)現(xiàn) slave 節(jié)點(diǎn)
確定主從關(guān)系
每 2 秒通過發(fā)布/訂閱功能獲取其他哨兵的信息
每 1 秒通過向其他節(jié)點(diǎn)發(fā)送 ping 命令,進(jìn)行心跳檢測,判斷是否下線(Monitor)
選舉領(lǐng)導(dǎo)者哨兵節(jié)點(diǎn)
當(dāng)主節(jié)點(diǎn)被判斷客戶下線后,各個哨兵會協(xié)商,選舉出一個領(lǐng)導(dǎo)者哨兵節(jié)點(diǎn),并由改領(lǐng)導(dǎo)者節(jié)點(diǎn)對其進(jìn)行故障轉(zhuǎn)移操作。
監(jiān)視改主節(jié)點(diǎn)的哨兵都有可能成為領(lǐng)導(dǎo)者,選舉使用的算法是 Raft 算法,Raft 算法的思路是先到先得,即在一輪選舉中,哨兵 A 向哨兵 B 發(fā)送稱為領(lǐng)導(dǎo)者的申請,如果哨兵 B 沒有同意過其他哨兵,則會同意哨兵 A 成為領(lǐng)導(dǎo)者,選舉的過程很快,通常,誰先完成客觀下線,一般就能成為領(lǐng)導(dǎo)者,成為領(lǐng)導(dǎo)者后,就可以開始進(jìn)行故障轉(zhuǎn)移,即選舉新的主節(jié)點(diǎn)
選舉主節(jié)點(diǎn)原則
首先過濾掉不健康的從節(jié)點(diǎn)(ping 不通,或延遲比較久)
過濾掉響應(yīng)慢的從節(jié)點(diǎn)
過濾掉與 master 斷開時間最久的從節(jié)點(diǎn)
優(yōu)先原則
優(yōu)先選擇優(yōu)先級最高(replica-priority)的從節(jié)點(diǎn)
如果優(yōu)先級無法區(qū)分,則選擇復(fù)制偏移量最大的從節(jié)點(diǎn)(偏移量越大,表示與主機(jī)數(shù)據(jù)越接近)
如果仍無法區(qū)分,則選擇 runid 最小的從節(jié)點(diǎn)
實(shí)踐建議
哨兵節(jié)點(diǎn)數(shù)量應(yīng)不止一個,一方面增加哨兵節(jié)點(diǎn)的冗余,避免哨兵節(jié)點(diǎn)成為高可用的瓶頸,另一方面減少對客觀下線的誤判,此外哨兵節(jié)點(diǎn)通常放再不同的物理主機(jī)上
哨兵節(jié)點(diǎn)數(shù)量最好是奇數(shù),以便于哨兵通過投票選出“領(lǐng)導(dǎo)者”以及對客觀下線的“決策”
各個哨兵節(jié)點(diǎn)配置應(yīng)保持一致,包括硬件、網(wǎng)絡(luò)等參數(shù),此外應(yīng)保證時間的準(zhǔn)確性
缺點(diǎn)
存儲能力受到單機(jī)的限制
寫操作無法負(fù)載均衡
4、Redis Cluster 高可用集群
通過集群,Redis 解決了寫操作無法負(fù)載均衡,以及存儲能力受到單機(jī)限制的問題,實(shí)現(xiàn)了較為完善的高可用方案

Redis Cluster 集群是一個由多個主從節(jié)點(diǎn)群組成的分布式服務(wù)器群(至少要有 3 個主節(jié)點(diǎn)),它具有復(fù)制、高可用和分片特性。Redis Custer 集群不需要 sentinel 哨兵也能完成節(jié)點(diǎn)移除和故障轉(zhuǎn)移的功能。需要將每個節(jié)點(diǎn)設(shè)置成集群模式,這種集群模式?jīng)]有中心節(jié)點(diǎn),可水平擴(kuò)展,據(jù)官方文檔稱可以線性擴(kuò)展到 1000 節(jié)點(diǎn)。Redis Cluster 集群的性能和高可用性均優(yōu)于之前版本的哨兵模式,且集群配置非常簡單
集群搭建
redis-cli --cluster help
原生搭建
基本配置文件
bind 0.0.0.0
port 7000
daemonize yes
pidfile /home/eric/redis-cluster/redis7000/redis_7000.pid
logfile "/home/eric/redis-cluster/redis7000/redis_7000.log"
dir /home/eric/redis-cluster/redis7000/
masterauth 123456
requirepass 123456
配置開啟 cluster 節(jié)點(diǎn)(集群配置)
cluster-enabled yes(啟動集群模式)
cluster-config-file nodes-7000.conf(節(jié)點(diǎn)配置文件,這里 7000 最好和 port 對應(yīng)上)
cluster-require-full-coverage no (主機(jī)宕機(jī)了,且沒有從機(jī)可進(jìn)行故障轉(zhuǎn)移,那么整個集群是否可以繼續(xù)使用)# 替換配置文件
sed 's/7000/7001/g' redis7000/redis.conf > redis7001/redis.conf
sed 's/7000/7002/g' redis7000/redis.conf > redis7002/redis.conf
...
sed 's/7000/7005/g' redis7000/redis.conf > redis7005/redis.conf
# 啟動
redis-server ./redis7000/redis.conf
redis-server ./redis7001/redis.conf
redis-server ./redis7002/redis.conf
redis-server ./redis7003/redis.conf
redis-server ./redis7004/redis.conf
redis-server ./redis7005/redis.conf
# 檢查進(jìn)程啟動情況
ps -ef | grep redis
eric 252 1 0 13:32 ? 00:00:00 redis-server 0.0.0.0:7000 [cluster]
eric 257 1 0 13:32 ? 00:00:00 redis-server 0.0.0.0:7001 [cluster]
eric 262 1 0 13:32 ? 00:00:00 redis-server 0.0.0.0:7002 [cluster]
eric 267 1 0 13:32 ? 00:00:00 redis-server 0.0.0.0:7003 [cluster]
eric 272 1 0 13:32 ? 00:00:00 redis-server 0.0.0.0:7004 [cluster]
eric 277 1 0 13:32 ? 00:00:00 redis-server 0.0.0.0:7005 [cluster]
eric 282 7 0 13:32 tty1 00:00:00 grep --color=auto redis
# 測試連接
redis-cli -h 127.0.0.1 -p 7000 -a 123456
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
127.0.0.1:7000> set k1 v1
(error) CLUSTERDOWN Hash slot not served // 沒有分配槽位,所以不能添加數(shù)據(jù)
127.0.0.1:7000> cluster nodes
7b777e4abce4e3427dd9a7a42721e46bdc42af88 :7000@17000 myself,master - 0 0 0 connected
meet
讓所有的集群相互認(rèn)識(通訊)
cluster meet ip port
cluster meet 127.0.0.1 7001
cluster meet 127.0.0.1 7002
指派槽
查看 crc16 算法算出 key 的槽位命令 cluster keyslot key
cluster keyslot key
16384/3 0-5461 5462-10922 10923-16383
16384/4 4096
cluster addslots slot(槽位下標(biāo))
分配主從
cluster replicate node-id
使用 redis-cli 配置集群
Redis Cluster 集群需要至少要三個 master 節(jié)點(diǎn),我們這里搭建三個 master 節(jié)點(diǎn),并且給每個 master 再搭建一個 slave 節(jié)點(diǎn),總共 6 個 Redis 節(jié)點(diǎn)。
由于節(jié)點(diǎn)數(shù)較多,這里采用在一臺機(jī)器上創(chuàng)建 6 個 Redis 實(shí)例,并將這 6 個 Redis 實(shí)例配置成集群模式,所以這里搭建的是偽集群模式,當(dāng)然真正的分布式集群的配置方法幾乎一樣
查看集群命令:
redis-cli --cluster help
1、分配主機(jī)
create host1:port1 ... hostN:portN --cluster-replicas <arg>
redis-cli --cluster create 127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 --cluster-replicas 1 -a 123456
其中--cluster-replicas 1 表示 6 個節(jié)點(diǎn)按 1:1 分配,那么就是 3 個 master 節(jié)點(diǎn),3 個 slave 節(jié)點(diǎn),如果 --cluster-replicas 2 則 6 個節(jié)點(diǎn)表示按照 1:2 分配,那么就會使 2 個 master 節(jié)點(diǎn),4 個 slave 節(jié)點(diǎn),如果這樣依賴,則不滿足最少 3 個 master 節(jié)點(diǎn),就會失敗
主節(jié)點(diǎn):取最前面的
從節(jié)點(diǎn):依次取后面的,做為主節(jié)點(diǎn)的從節(jié)點(diǎn),隨機(jī)分配
槽位:根據(jù)主機(jī)數(shù)量,平均分配
查看節(jié)點(diǎn)狀態(tài)
redis-cli -h 127.0.0.1 -p 7000 -a 123456 cluster nodes
Warning: Using a password with '-a' or '-u' option on the command line interface may not be safe.
7fd64ce350df005c963ab355e7e5551b00c6dc7e 127.0.0.1:7002@17002 master - 0 1599406449000 2 connected 5461-10922
fe9183edd29a1c1c9a34eccc1b638f46a1c59aab 127.0.0.1:7005@17005 slave 87e7956a0513ea3da005c78224d728e3cd77f609 0 1599406448000 3 connected
87e7956a0513ea3da005c78224d728e3cd77f609 127.0.0.1:7003@17003 master - 0 1599406450945 3 connected 10923-16383
4f9d051cb178f5078df51eb6e52f1be3ae30a2d2 127.0.0.1:7001@17001 myself,master - 0 1599406449000 1 connected 0-5460
43d57aff953c58a223a528c689ec6a397eb85481 127.0.0.1:7006@17006 slave 4f9d051cb178f5078df51eb6e52f1be3ae30a2d2 0 1599406449938 1 connected
3f468212653a046626e2869aeff46ee9bcb61c4a 127.0.0.1:7004@17004 slave 7fd64ce350df005c963ab355e7e5551b00c6dc7e 0 1599406451954 2 connected以集群方式登錄
-C 代表以集群方式登錄,寫值成功后會自動跳轉(zhuǎn)到所屬主機(jī),否則就是以正常方式登錄,當(dāng)寫數(shù)據(jù)時,可能會提示 hash 計(jì)算的槽位,并不屬于當(dāng)前主機(jī)
redis-cli -h 127.0.0.1 -p 7000 -a 123456 -c
127.0.0.1:7000> set k1 v1
-> Redirected to slot [12706] located at 127.0.0.1:7002
OK
127.0.0.1:7002> keys *
1) "k1"
2、擴(kuò)容和縮容
啟動需要加入集群的節(jié)點(diǎn)
redis-server ./redis7006/redis.conf
redis-server ./redis7007/redis.conf
添加節(jié)點(diǎn)
add-node new_host:new_port existing_host:existing_port --cluster-slave --cluster-master-id <arg>
添加 master 節(jié)點(diǎn)
redis-cli --cluster add-node 127.0.0.1:7006 127.0.0.1:7000 -a 123456
添加 slave 節(jié)點(diǎn)
redis-cli --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 --cluster-slave --cluster-master-id master的ID -a 123456
分配槽位
reshard host:port --cluster-from <arg> --cluster-to <arg> --cluster-slots <arg> --cluster-yes --cluster-timeout <arg> --cluster-pipeline <arg> --cluster-replace
利用導(dǎo)航模式分配,根據(jù)提示一步一步完成
要分配的槽位數(shù) -> 接收槽位的 node id -> 分配策略(all / 指定要分出槽位的 node id)-> done -> yes
all:所有節(jié)點(diǎn)平均分配
輸入要分配的 node id -> 繼續(xù)輸入要分配的 node id -> ... -> done
redis-cli --cluster reshard 127.0.0.1:7000 -a 123456刪除節(jié)點(diǎn)
1、先對槽位進(jìn)行縮容
redis-cli --cluster reshard 127.0.0.1:7000
--cluster-from 要遷移的節(jié)點(diǎn)ID --cluster-to 要接收的節(jié)點(diǎn)ID --cluster-slots 要遷出的槽位數(shù)量
2、再進(jìn)行刪除
刪除的時候,通常都是先刪除從節(jié)點(diǎn),然后再刪除主節(jié)點(diǎn),否則會進(jìn)行故障轉(zhuǎn)移,發(fā)生不必要的資源浪費(fèi)
redis-cli --cluster del-node 127.0.0.1:7000 要刪除的節(jié)點(diǎn)ID -a 123456
source: https://www.yuque.com/nashihuakai/qlwgtg/zk5qz7
推薦閱讀
國產(chǎn)小眾瀏覽器因屏蔽視頻廣告,被索賠100萬(后續(xù))
年輕人“不講武德”:因看黃片上癮,把網(wǎng)站和786名女主播起訴了
關(guān)于程序員大白
程序員大白是一群哈工大,東北大學(xué),西湖大學(xué)和上海交通大學(xué)的碩士博士運(yùn)營維護(hù)的號,大家樂于分享高質(zhì)量文章,喜歡總結(jié)知識,歡迎關(guān)注[程序員大白],大家一起學(xué)習(xí)進(jìn)步!


