Redis集羣原理和搭建

一個系統建立集羣主要需要解決兩個問題:數據同步問題和集羣容錯問題。

1. Naive方案

一個簡單粗暴的方案是部署多臺一模一樣的Redis服務,再用負載均衡來分攤壓力以及監控服務狀態。這種方案的優勢在於容錯簡單,只要有一臺存活,整個集羣就仍然可用。但是它的問題在於保證這些Redis服務的數據一致時,會導致大量數據同步操作,反而影響性能和穩定性。

2. Redis集羣方案

Redis集羣方案基於分而治之的思想。Redis中數據都是以Key-Value形式存儲的,而不同Key的數據之間是相互獨立的。因此可以將Key按照某種規則劃分成多個分區,將不同分區的數據存放在不同的節點上。
這個方案類似數據結構中哈希表的結構。在Redis集羣的實現中,使用哈希算法(公式是CRC16(Key) mod 16383)將Key映射到0~16383範圍的整數。這樣每個整數對應存儲了若干個Key-Value數據,這樣一個整數對應的抽象存儲稱爲一個槽(slot)。每個Redis Cluster的節點——準確講是master節點——負責一定範圍的槽,所有節點組成的集羣覆蓋了0~16383整個範圍的槽。
據說任何計算機問題都可以通過增加一箇中間層來解決。槽的概念也是這麼一層。它介於數據和節點之間,簡化了擴容和收縮操作的難度。數據和槽的映射關係由固定算法完成,不需要維護,節點只需維護自身和槽的映射關係。
圖爲槽到節點的映射
在這裏插入圖片描述

3. Slave

上面的方案只是解決了性能擴展的問題,集羣的故障容錯能力並沒有提升。提高容錯能力的方法一般爲使用某種備份/冗餘手段。負責一定數量的槽的節點被稱爲master節點。爲了增加集羣穩定性,每個master節點可以配置若干個備份節點——稱爲slave節點。
Slave節點一般作爲冷備份保存master節點的數據,在master節點宕機時替換master節點。在一些數據訪問壓力比較大的情況下,slave節點也可以提供讀取數據的功能,不過slave節點的數據實時性會略差一下。而寫數據的操作則只能通過master節點進行。

4.請求重定向

當Redis節點接收到對某個key的命令時,如果這個key對應的槽不在自己的負責範圍內,則返回MOVED重定向錯誤,通知客戶端到正確的節點去訪問數據。
如果頻繁出現重定向錯誤,勢必會影響訪問的性能。由於從key映射到槽的算法是固定公開的,客戶端可以在內部維護槽到節點的映射關係,訪問數據時可以自己通過key計算出槽,然後找到正確的節點,減少重定向錯誤。目前大部分開發語言的Redis客戶端都會實現這個策略。

5. 節點通信

儘管不同節點存儲的數據相互獨立,這些節點仍然需要相互通信以同步節點狀態信息。Redis集羣採用P2P的Gossip協議,節點之間不斷地通信交換信息,最終所有節點的狀態都會達成一致。

常用的Gossip消息有下面幾種:
ping消息:每個節點不斷地向其他節點發起ping消息,用於檢測節點是否在線和交換節點狀態信息。
pong消息:收到ping、meet消息時的響應消息。
meet消息:新節點加入消息。
fail消息:節點下線消息。
forget消息:忘記節點消息,使一個節點下線。這個命令必須在60秒內在所有節點執行,否則超過60秒後該節點重新參與消息交換。實踐中不建議直接使用forget命令來操作節點下線。

6. 節點下線

當某個節點出現問題時,需要一定的傳播時間讓多數master節點認爲該節點確實不可用,才能標記標記該節點真正下線。
Redis集羣的節點下線包括兩個環節:
主觀下線:當節點A在cluster-node-timeout時間內和節點B通信(ping-pong消息)一直失敗,則節點A認爲節點B不可用,標記爲主觀下線,並將狀態消息傳播給其他節點。

客觀下線:當一個節點被集羣內多數master節點標記爲主觀下線後,則觸發客觀下線流程,標記該節點真正下線。

7.故障恢復

一個持有槽的master節點客觀下線後,集羣會從slave節點中選出一個提升爲master節點來替換它。Redis集羣使用選舉-投票的算法來挑選slave節點。
一個slave節點必須獲得包括故障的master節點在內的多數master節點的投票後才能被提升爲master節點。假設集羣規模爲3主3從,則必須至少有2個主節點存活才能執行故障恢復。
如果部署時將2個主節點部署到同一臺服務器上,則該服務器不幸宕機後集羣無法執行故障恢復。
默認情況下,Redis集羣如果有master節點不可用,即有一些槽沒有負責的節點,則整個集羣不可用。
也就是說當一個master節點故障,到故障恢復的這段時間,整個集羣都處於不可用的狀態。這對於一些業務來說是不可忍受的。
可以在配置中將cluster-require-full-coverage配置爲no,那麼master節點故障時只會影響訪問它負責的相關槽的數據,不影響對其他節點的訪問。

8. 搭建集羣

啓動新節點
修改Redis配置文件以啓動集羣模式:
圖爲發送meet消息:
在這裏插入圖片描述

9. 分配槽

在這裏插入圖片描述
上一步執行完後我們得到的是一個還沒有負責任何槽的“空”集羣。
爲了使集羣可用,我們需要將16384個槽都分配到master節點數。
在客戶端執行cluster add addslots {}命令,將~範圍的槽都分配給當前客戶端所連接的節點。將所有的槽都分配給master節點後,執行cluster nodes命令,查看各個節點負責的槽,以及節點的ID。

接下來還需要分配slave節點。使用客戶端連接待分配的slave節點,執行cluster replicate 命令,將該節點分配爲指定的master節點的備份。

10.使用命令直接創建集羣

在Redis 5版本中redis-cli客戶端新增了集羣操作命令。
如下所示,直接使用命令創建一個3主3從的集羣:如果你用的是舊版本的Redis,可以使用官方提供的redis-trib.rb腳本來創建集羣:
如下所示,直接使用命令創建一個3主3從的集羣:
在這裏插入圖片描述
如果你用的是舊版本的Redis,可以使用官方提供的redis-trib.rb腳本來創建集羣:
在這裏插入圖片描述

11. 集羣伸縮

擴容

擴容操作與創建集羣操作類似,不同的在於最後一步是將槽從已有的節點遷移到新節點。

收縮

爲了安全刪除節點,Redis集羣只能下線沒有負責槽的節點。因此如果要下線有負責槽的master節點,則需要先將它負責的槽遷移到其他節點。
1、遷移槽。使用命令redis-cli --cluster reshard將待刪除節點的槽都遷移到其他節點。
2、忘記節點。使用命令redis-cli --cluster del-node刪除節點(內部使用forget消息實現)。

集羣配置工具

如果你的redis-cli版本低於5,那麼可以使用redis-trib.rb腳本來完成上面的命令。點擊這裏查看redis-cli和redis-trib.rb操作集羣的命令。

12. 持久化

Redis有RDB和AOF兩種持久化策略。這篇文章詳細講解了RDB和AOF持久化原理。

一個RDB持久化的坑

RDB持久化神坑:
a.即使設置了save ""試圖關閉RDB,然而RDB持久化仍然有可能會觸發。
b.從節點全量複製(比如新增從節點時),主節點觸發RDB持久化產生RDB文件。然後發送RDB文件給從節點。最後該從節點和對應的主節點都會有RDB文件。
C.執行shutdown時,如果沒有開啓AOF,也會觸發RDB持久化。
d.不管save如何設置,只要RDB文件存在,redis啓動時就會去加載該文件。

後果

a. 如果關閉了RDB持久化(以及AOF持久化),那麼當Redis重啓時,則會加載上一次從節點全量複製或者執行shutdown時保存的RDB文件。而這個RDB文件很可能是一份過時已久的數據。

b.Cluster模式下,Redis重啓並從RDB文件恢復數據後,如果沒有讀取到cluster-config-file中nodes的配置,則標記自己爲單獨的master並佔用從RDB中恢復的數據的Key對應的槽,導致此節點無法再加入其它集羣。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章