聊聊 Redis 的高可用
高可用性(HA),原本是系統(tǒng)的一個(gè)特性,旨在確保在高于平均水平的時(shí)間內(nèi)保持約定的運(yùn)行性能水平,通常是正常運(yùn)行時(shí)間。
Redis 作為一個(gè)內(nèi)存數(shù)據(jù)庫(kù),其數(shù)據(jù)通常存儲(chǔ)在內(nèi)存中,一旦發(fā)生故障,可能導(dǎo)致數(shù)據(jù)丟失或服務(wù)中斷,避免單點(diǎn)故障至關(guān)重要,這樣系統(tǒng)才能順利快速地恢復(fù)。
Redis 高可用 是指 Redis 通過一系列技術(shù)手段確保在面臨故障的情況下也能持續(xù)提供服務(wù)的能力。
圖片
由于我們現(xiàn)在已經(jīng)進(jìn)入了一個(gè)分布式系統(tǒng),并且需要考慮許多錯(cuò)誤,因此在這個(gè)拓?fù)浣Y(jié)構(gòu)中需要考慮一些新問題,以前簡(jiǎn)單的事情現(xiàn)在變得更加復(fù)雜。
為了保證 Redis 的高可用,它主要采用了以下三種手段:
- Redis 主從復(fù)制
- Redis 哨兵模式
- Redis 集群Cluster
接下來,我們逐個(gè)來分析看看。
1、Redis 主從復(fù)制
圖片
主從復(fù)制是 Redis 多機(jī)運(yùn)行中最基礎(chǔ)的功能,它是把多個(gè) Redis 節(jié)點(diǎn)組成一個(gè) Redis 集群,在這個(gè)集群當(dāng)中有一個(gè)主節(jié)點(diǎn)用來進(jìn)行數(shù)據(jù)的操作,其他從節(jié)點(diǎn)用于同步主節(jié)點(diǎn)的內(nèi)容,并且提供給客戶端進(jìn)行數(shù)據(jù)查詢。
Redis 主從同步分為:
- 全量復(fù)制:首次數(shù)據(jù)同步時(shí)
- 增量復(fù)制:只需把主從庫(kù)網(wǎng)絡(luò)斷連期間主庫(kù)收到的命令,同步給從庫(kù)
注意:在2.8版本之前只有全量復(fù)制,而2.8版本后有全量和增量復(fù)制
1.1 全量復(fù)制
全量復(fù)制主要的實(shí)施流程,包括以下幾個(gè)方面:
- 建立主從關(guān)系
當(dāng)我們啟動(dòng)多個(gè) Redis 實(shí)例的時(shí)候,它們相互之間就可以通過 replicaof(Redis 5.0 之前使用 slaveof)命令形成主庫(kù)和從庫(kù)的關(guān)系
/*
* 主:實(shí)例 1(ip:172.168.0.1)
* 從:實(shí)例 2(ip:172.168.0.2)
* 在從庫(kù)上執(zhí)行以下命令
*/
replicaof 172.16.0.1 6379
- 全量復(fù)制過程
1)主從庫(kù)間建立連接、協(xié)商同步
從庫(kù)給主庫(kù)發(fā)送 psync 命令,表示要進(jìn)行數(shù)據(jù)同步,主庫(kù)根據(jù)這個(gè)命令的參數(shù)來啟動(dòng)復(fù)制。
psync 命令包含了主庫(kù)的 runID 和復(fù)制進(jìn)度 offset 兩個(gè)參數(shù):
- runID,是每個(gè) Redis 實(shí)例啟動(dòng)時(shí)都會(huì)自動(dòng)生成的一個(gè)隨機(jī) ID,用來唯一標(biāo)記這個(gè)實(shí)例
- offset,此時(shí)設(shè)為 -1,表示第一次復(fù)制
2)主庫(kù)將所有數(shù)據(jù)同步給從庫(kù)
主庫(kù)執(zhí)行 bgsave 命令,生成 RDB 文件,接著將文件發(fā)給從庫(kù)。從庫(kù)接收到 RDB 文件后,會(huì)先清空當(dāng)前數(shù)據(jù)庫(kù),然后加載 RDB 文件。
在主庫(kù)將數(shù)據(jù)同步給從庫(kù)的過程中,主庫(kù)不會(huì)被阻塞,仍然可以正常接收請(qǐng)求,否則,Redis 的服務(wù)就被中斷了。但是,這些請(qǐng)求中的寫操作并沒有記錄到剛剛生成的 RDB 文件中。為了保證主從庫(kù)的數(shù)據(jù)一致性,主庫(kù)會(huì)在內(nèi)存中用專門的 replication buffer,記錄 RDB 文件生成后收到的所有寫操作。
3)主庫(kù)會(huì)把第二階段執(zhí)行過程中新收到的寫命令,再發(fā)送給從庫(kù)
當(dāng)主庫(kù)完成 RDB 文件發(fā)送后,就會(huì)把此時(shí) replication buffer 中的修改操作發(fā)給從庫(kù),從庫(kù)再重新執(zhí)行這些操作。
1.2 增量復(fù)制
此功能在 Redis 2.8 版本才引入,主要為了控制主從復(fù)制的成本開銷。網(wǎng)絡(luò)斷了之后,主從庫(kù)會(huì)采用增量復(fù)制的方式繼續(xù)同步。
先來看一個(gè)概念: replication_backlog 復(fù)制積壓緩沖區(qū)。
此命令一方面會(huì)傳輸給從節(jié)點(diǎn),另外還會(huì)記錄在這個(gè)復(fù)制積壓緩沖區(qū)里。Redis 使用一個(gè)環(huán)形緩沖區(qū)的結(jié)構(gòu)保存最近的一些命令。在緩沖區(qū)中,對(duì)字節(jié)進(jìn)行編號(hào),這個(gè)編號(hào)在 Redis 中叫復(fù)制偏移量。
圖片
是否滿足增量同步的條件:
- 從節(jié)點(diǎn) replid 和 主節(jié)點(diǎn)的 replid 相同
- 復(fù)制偏移量 offset 在復(fù)制積壓緩沖區(qū)的 backlog_off 和 offset 范圍之間。
2、Redis 哨兵模式
哨兵模式是redis高可用的實(shí)現(xiàn)方式之一,使用一個(gè)或者多個(gè)哨兵(Sentinel)實(shí)例組成的系統(tǒng),對(duì)redis節(jié)點(diǎn)進(jìn)行監(jiān)控,在主節(jié)點(diǎn)出現(xiàn)故障的情況下,能將從節(jié)點(diǎn)中的一個(gè)升級(jí)為主節(jié)點(diǎn),進(jìn)行故障轉(zhuǎn)義,保證系統(tǒng)的可用性。
圖片
2.1 哨兵實(shí)現(xiàn)了什么功能呢?
- 監(jiān)控(Monitoring):確保主從實(shí)例是否運(yùn)作正常
- 自動(dòng)故障轉(zhuǎn)移(Automatic failover):如果主實(shí)例不可用并且足夠多的(法定數(shù)量)節(jié)點(diǎn)同意這是真的,Sentinel 節(jié)點(diǎn)可以啟動(dòng)故障轉(zhuǎn)移
- 配置提供者(Configuration provider):客戶端在初始化時(shí),通過連接哨兵來獲得當(dāng)前Redis服務(wù)的主節(jié)點(diǎn)地址
- 通知(Notification):哨兵可以將故障轉(zhuǎn)移的結(jié)果發(fā)送給客戶端
以這種方式使用 Redis Sentinel 可以進(jìn)行故障檢測(cè)。此檢測(cè)涉及多個(gè)哨兵進(jìn)程同意當(dāng)前主實(shí)例不再可用。這個(gè)協(xié)議過程稱為 Quorum。這可以提高魯棒性并防止一臺(tái)機(jī)器行為異常導(dǎo)致無法訪問主 Redis 節(jié)點(diǎn)。
2.2 自動(dòng)故障轉(zhuǎn)移
- 主觀下線
哨兵(Sentinel)節(jié)點(diǎn)會(huì)每秒一次的頻率向建立了命令連接的實(shí)例發(fā)送 PING 命令,如果在down-after-milliseconds 毫秒內(nèi)沒有做出有效響應(yīng),包括(PONG/LOADING/MASTERDOWN)以外的響應(yīng),哨兵就會(huì)將該實(shí)例在本結(jié)構(gòu)體中的狀態(tài)標(biāo)記為 SRI_S_DOWN 主觀下線。
- 客觀下線
當(dāng)一個(gè)哨兵節(jié)點(diǎn)發(fā)現(xiàn)主節(jié)點(diǎn)處于主觀下線狀態(tài)時(shí),會(huì)向其他的哨兵節(jié)點(diǎn)發(fā)出詢問,該節(jié)點(diǎn)是不是已經(jīng)主觀下線了。
如果超過配置參數(shù) quorum 個(gè)節(jié)點(diǎn)認(rèn)為是主觀下線時(shí),該哨兵節(jié)點(diǎn)就會(huì)將自己維護(hù)的結(jié)構(gòu)體中該主節(jié)點(diǎn)標(biāo)記為 SRI_O_DOWN 客觀下線 詢問命令:
SENTINEL is-master-down-by-addr <current_epoch> <run_id>
- leader選舉
在認(rèn)為主節(jié)點(diǎn)客觀下線的情況下,哨兵節(jié)點(diǎn)節(jié)點(diǎn)間會(huì)發(fā)起一次選舉,命令如下:
SENTINEL is-master-down-by-addr <current_epoch> <run_id>
只是這次會(huì)將自己的 run_id 帶進(jìn)去,希望接受者將自己設(shè)置為主節(jié)點(diǎn)。
如果超過半數(shù)以上的節(jié)點(diǎn)返回將該節(jié)點(diǎn)標(biāo)記為 leader 的情況下,會(huì)由該 leader 對(duì)故障進(jìn)行轉(zhuǎn)移。
- 故障轉(zhuǎn)移
在從節(jié)點(diǎn)中挑選出新的主節(jié)點(diǎn)
a. 通訊正常
b. 優(yōu)先級(jí)排序
c. 優(yōu)先級(jí)相同是選擇offset最大的
將該節(jié)點(diǎn)設(shè)置成新的主節(jié)點(diǎn) SLAVEOF no one,并確保在后續(xù)的INGO命令時(shí),該節(jié)點(diǎn)返回狀態(tài)為master
將其他的從節(jié)點(diǎn)設(shè)置成從新的主節(jié)點(diǎn)復(fù)制, SLAVEOF命令
將舊的主節(jié)點(diǎn)變成新的主節(jié)點(diǎn)的從節(jié)點(diǎn)
3、Redis 集群Cluster
Cluster 即 集群模式。類似MySQL,Redis 集群也是一種分布式數(shù)據(jù)庫(kù)方案,集群通過分片(sharding)模式來對(duì)數(shù)據(jù)進(jìn)行管理,并具備分片間數(shù)據(jù)復(fù)制、故障轉(zhuǎn)移和流量調(diào)度的能力。
圖片
Redis Cluster 允許 Redis 的水平擴(kuò)展。
3.1 集群Cluster 介紹
Redis 集群將數(shù)據(jù)劃分為 16384(2的14次方)個(gè)哈希槽(slots),如果你有多個(gè)實(shí)例節(jié)點(diǎn),那么每個(gè)實(shí)例節(jié)點(diǎn)將管理其中一部分的槽位,槽位的信息會(huì)存儲(chǔ)在各自所歸屬的節(jié)點(diǎn)中。以下圖為例,該集群有4個(gè) Redis 節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)負(fù)責(zé)集群中的一部分?jǐn)?shù)據(jù),數(shù)據(jù)量可以不均勻。比如性能好的實(shí)例節(jié)點(diǎn)可以多分擔(dān)一些壓力。
圖片
一個(gè)Redis集群一共有16384個(gè)哈希槽,你可以有1 ~ n個(gè)節(jié)點(diǎn)來分配這些哈希槽,可以不均勻分配,每個(gè)節(jié)點(diǎn)可以處理0個(gè) 到至多 16384 個(gè)槽點(diǎn)。當(dāng)16384個(gè)哈希槽都有節(jié)點(diǎn)進(jìn)行管理的時(shí)候,集群處于online 狀態(tài)。同樣的,如果有一個(gè)哈希槽沒有被管理到,那么集群處于offline狀態(tài)。
上面圖中4個(gè)實(shí)例節(jié)點(diǎn)組成了一個(gè)集群,集群之間的信息通過 Gossip協(xié)議 進(jìn)行交互,這樣就可以在某一節(jié)點(diǎn)記錄其他節(jié)點(diǎn)的哈希槽(slots)的分配情況。
3.2 Cluster模式擴(kuò)展
單機(jī)的吞吐無法承受持續(xù)擴(kuò)增的流量的時(shí)候,最好的辦法是從橫向(scale out) 和 縱向(scale up)兩方面進(jìn)行擴(kuò)展:
- 縱向擴(kuò)展(scale up):將單個(gè)實(shí)例的硬件資源做提升,比如 CPU核數(shù)量、內(nèi)存容量、SSD容量
- 橫向擴(kuò)展(scale out):橫向擴(kuò)增 Redis 實(shí)例數(shù),這樣每個(gè)節(jié)點(diǎn)只負(fù)責(zé)一部分?jǐn)?shù)據(jù)就可以(典型的分治思維)
圖片
4、總結(jié)
高可用一般來說有兩個(gè)含義:1)數(shù)據(jù)盡量不丟失,2)保證服務(wù)盡可能可用。
AOF 和 RDB 數(shù)據(jù)持久化保證了數(shù)據(jù)盡量不丟失,而多節(jié)點(diǎn)來保證服務(wù)盡可能提供服務(wù)。單個(gè)節(jié)點(diǎn)的系統(tǒng)吞吐量有限,容量也有限,缺點(diǎn)明顯,一旦發(fā)生故障會(huì)導(dǎo)致服務(wù)不可用。
- 使用讀寫分離之前,可以考慮其他方法增加Redis的讀負(fù)載能力:如盡量?jī)?yōu)化主節(jié)點(diǎn)(減少慢查詢、減少持久化等其他情況帶來的阻塞等)提高負(fù)載能力;使用Redis集群同時(shí)提高讀負(fù)載能力和寫負(fù)載能力等
- 哨兵模式已經(jīng)實(shí)現(xiàn)了故障自動(dòng)轉(zhuǎn)移的能力,但業(yè)務(wù)規(guī)模的不斷擴(kuò)展,用戶量膨脹,并發(fā)量持續(xù)提升,會(huì)出現(xiàn)了 Redis 響應(yīng)慢的情況
- 使用 Redis Cluster 集群,主要解決了大數(shù)據(jù)量存儲(chǔ)導(dǎo)致的各種慢問題,同時(shí)也便于橫向拓展。在面對(duì)千萬級(jí)甚至億級(jí)別的流量的時(shí)候,很多是在千百臺(tái)的實(shí)例節(jié)點(diǎn)組成的集群上進(jìn)行流量調(diào)度、服務(wù)治理的。