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