Redis 集羣模式原理與實戰

1、集羣模式介紹

Redis Cluster 是 Redis 官方提供的分佈式實現,在 Redis 3.0 版本正式推出,經過集羣模式能夠擴展單機的性能瓶頸,同時也能夠經過橫向擴展來實現擴容。此外,Redis 集羣模式還提供了副本遷移機制,用於保證數據的安全和提升集羣的容錯能力,從而實現高可用。node

1.1 數據分區

Redis Cluster 採用虛擬槽進行分區,槽是集羣內數據管理和遷移的基本單位。全部的鍵根據哈希函數映射到 16384 個整數槽內,每一個節點負責維護一部分槽及槽上的數據,計算公式以下:git

HASH_SLOT = CRC16(key) mod 16384
複製代碼

假設如今有一個 6 個節點的集羣,分別有 3 個 Master 點和 3 個 Slave 節點,槽會盡可能均勻的分佈在全部 Master 節點上。數據通過散列後存儲在指定的 Master 節點上,以後 Slave 節點會進行對應的複製操做。這裏再次說明一下槽只是一個虛擬的概念,並非數據存放的實際載體。github

1.2 節點通信

在 Redis 分佈式架構中,每一個節點都存儲有整個集羣全部節點的元數據信息,這是經過 P2P 的 Gossip 協議來實現的。集羣中的每一個節點都會單獨開闢一個 TCP 通道,用於節點之間彼此通訊,通訊端口號在基礎端口上加 10000;每一個節點按期經過特定的規則選擇部分節點發送 ping 消息,接收到 ping 信息的節點用 pong 消息做爲響應,經過一段時間的彼此通訊,最終全部節點都會達到一致的狀態,每一個節點都會知道整個集羣所有節點的狀態信息,從而到達集羣狀態同步的目的。redis

1.3 請求路由

1. 請求重定向

在集羣模式下,Redis 接收到命令時會先計算鍵對應的槽,而後根據槽找出對應的目標節點,若是目標節點就是此時所在的節點,則直接進行處理,不然返回 MOVED 重定向消息給客戶端,通知客戶端去正確的節點上執行操做。shell

2. Smart 客戶端

Redis 的大多數客戶端都採用 Smart 客戶端支持集羣協議, Smart 客戶端會在內部緩存槽與節點之間的映射關係,從而在本機就能夠查找到正確的節點,這樣能夠保證 IO 效率的最大化。若是客戶端還接收到 MOVED 重定向的消息,則表明客戶端內部的緩存已經失效,此時客戶端會去從新獲取映射關係而後刷新本地緩存。緩存

3. ASK 重定向

當集羣處於擴容階段時,此時槽上的數據可能正在從源節點遷移到目標節點,在這個期間可能出現一部分數據在源節點, 而另外一部分在目標節點狀況。此時若是源節點接收到命令並判斷出鍵對象不存在, 說明其可能存在於目標節點上, 這時會返回給客戶端 ASK 重定向異常。安全

ASK 重定向與 MOVED 重定向的區別在於:收到 ASK 重定向時說明集羣正在進行數據遷移, 客戶端沒法知道何時遷移完成,所以只是臨時性的重定向, 客戶端不會更新映射緩存。 可是 MOVED 重定向說明鍵對應的槽已經明確遷移到新的節點, 所以須要更新映射緩存。bash

1.4 故障發現

因爲 Redis 集羣的節點間都保持着定時通信,某個節點向另一個節點發送 ping 消息,若是正常接受到 pong 消息,此時會更新與該節點最後一次的通信時間記錄,若是以後沒法正常接受到 pong 消息,而且判斷當前時間與最後一次通信的時間超過 cluster-node-timeout ,此時會對該節點作出主觀下線的判斷。架構

當作出主觀下線判斷後,節點會把這個判斷在集羣內傳播,經過 Gossip 消息傳播, 集羣內節點不斷收集到故障節點的下線報告。 當半數以上持有槽的主節點都標記某個節點是主觀下線時, 觸發客觀下線流程。 這裏須要注意的是隻有持有槽主節點纔有權利作出主觀下線的判斷,由於集羣模式下只有處理槽的主節點才負責讀寫請求和維護槽等關鍵信息, 而從節點只進行主節點數據和狀態信息的複製。併發

1.5 故障恢復

1. 資格檢查

每一個從節點都要檢查最後與主節點斷線時間, 判斷是否有資格替換故障的主節點。 若是從節點與主節點斷線時間超過 cluster-node-time*cluster-slave-validity-factor,則當前從節點不具有故障轉移資格。 這兩個參數能夠在 redis.conf 中進行修改,默認值分別爲 15000 和 10。

2. 準備選舉

當從節點符合故障轉移資格後, 更新觸發故障選舉的時間, 只有到達該時間後才能執行後續流程。在這一過程當中,Redis 會比較每一個符合資格的從節點的複製偏移量,而後讓複製偏移量大(即數據更加完整)的節點優先發起選舉。

3. 選舉投票

從節點每次發起投票時都會自增集羣的全局配置紀元,全局配置紀元是一個只增不減的整數。以後會在集羣內廣播選舉消息,只有持有槽的主節點纔會處理故障選舉消息,而且每一個持有槽的主節點在一個配置紀元內只有惟一的一張選票。假設集羣內有 N 個持有槽的主節點,當某個從節點得到 N/2+1 張選票則表明選舉成功。若是在開始投票以後的 cluster-node-timeout*2 時間內沒有從節點獲取足夠數量的投票, 則本次選舉做廢,從節點會對配置紀元自增併發起下一輪投票, 直到選舉成功爲止。

4. 替換主節點

當從節點收集到足夠的選票以後,就會觸發替換主節點操做:

  • 當前從節點取消複製變爲主節點。
  • 執行 clusterDelSlot 操做撤銷原主節點負責的槽, 並執行 clusterAddSlot 把這些槽委派給本身。
  • 向集羣廣播本身的 pong 消息,通知集羣內的其餘節點本身已經成爲新的主節點。

2、集羣模式搭建

2.1 節點配置

拷貝6份 redis.conf,分別命名爲 redis-6479.conf ~ redis-6484.conf,須要修改的配置項以下:

# redis-6479.conf
port 6479
# 以守護進程的方式啓動
daemonize yes  
# 當Redis以守護進程方式運行時,Redis會把pid寫入該文件
pidfile /var/run/redis_6479.pid  
logfile 6479.log
dbfilename dump-6479.rdb
dir /home/redis/data/
# 開啓集羣模式
cluster-enabled yes 
# 節點超時時間,單位毫秒
cluster-node-timeout 15000
# 集羣內部配置文件
cluster-config-file nodes-6479.conf
 # redis-6480.conf
port 6480
daemonize yes  
pidfile /var/run/redis_6480.pid  
logfile 6480.log
dbfilename dump-6480.rdb
dir /home/redis/data/
cluster-enabled yes 
cluster-node-timeout 15000
cluster-config-file nodes-6480.conf

..... 其餘配置相似,修改全部用到端口號的地方
複製代碼

2.2 啓動集羣

啓動全部 Redis 節點,啓動後使用 ps -ef | grep redis 查看進程,輸出應以下:

接着須要使用如下命令建立集羣,集羣節點之間會開始進行通信,並完成槽的分配:
redis-cli --cluster create 127.0.0.1:6479 127.0.0.1:6480 127.0.0.1:6481 \
127.0.0.1:6482 127.0.0.1:6483  127.0.0.1:6484 --cluster-replicas 1
複製代碼

執行後輸出以下:M 開頭的表示持有槽的主節點,S 開頭的表示從節點,每一個節點都有一個惟一的 ID。最後一句輸出表示全部的槽都已經分配到主節點上,此時表明集羣搭建成功。

2.3 集羣完整性校驗

集羣完整性指全部的槽都分配到存活的主節點上, 只要16384個槽中有一個沒有分配給節點則表示集羣不完整。 可使用如下命令進行檢測, check 命令只須要給出集羣中任意一個節點的地址就能夠完成整個集羣的檢查工做:

redis-cli --cluster check 127.0.0.1:6479
複製代碼

2.4 關於版本差別的說明

若是你使用的是 Redis 5,能夠和上面的示例同樣,直接使用嵌入到 redis-cli 中的 Redis Cluster 命令來建立和管理集羣。

若是你使用的是 Redis 3 或 4,則須要使用 redis-trib.rb 工具,此時須要預先準備 Ruby 環境和安裝 redis gem。

3、集羣伸縮

Redis 集羣提供了靈活的節點擴容和縮容方案,能夠在不影響集羣對外服務的狀況下,進行動態伸縮。

3.1 集羣擴容

這裏準備兩個新的節點 6485 和 6486,配置和其餘節點一致,配置完成後進行啓動。集羣擴容的命令爲 add-node,第一個參數爲須要加入的新節點,第二個參數爲集羣中任意節點,用於發現集羣:

redis-cli --cluster add-node 127.0.0.1:6485 127.0.0.1:6479
複製代碼

成功加入集羣后,可使用 cluster nodes 命令查看集羣狀況。不作任何特殊指定,默認加入集羣的節點都是主節點,可是集羣並不會爲分配任何槽。以下圖所示,其餘 master 節點後面都有對應的槽的位置信息,但新加入的 6485 節點則沒有,因爲沒有負責的槽,因此該節點此時不能進行任何讀寫操做:

redis-cli -h 127.0.0.1 -p 6479 cluster nodes
複製代碼

想要讓新加入的節點可以進行讀寫操做,可使用 reshard 命令爲其分配槽,這裏咱們將其餘三個主節點上的槽遷移一部分到 6485 節點上,這裏一共遷移 4096 個槽,即 16384 除以 4 。 cluster-from 用於指明槽的源節點,能夠爲多個,cluster-to 爲槽的目標節點,cluster-slots 爲須要遷移的槽的總數:

redis-cli --cluster reshard 127.0.0.1:6479 \
--cluster-from fd35b17ace0f15314ed3b3d4f8ff4da08e11b89d,ebd0425db25b8bcf843fee9826755848e23a895a,98a175734db4a106ae676dc403f39b2783640789 \
--cluster-to 819f867afd1da1acfb1a528d3efa91cffb02ba97 \
--cluster-slots 4096 --cluster-yes
複製代碼

遷移後,再次使用 cluster nodes 命令能夠查看到此時 6485 上已經有其餘三個主節點上遷移過來的槽:

爲保證高可用,能夠爲新加入的主節點添加從節點,命令以下。add-node 接收兩個參數,第一個爲須要添加的從節點,第二個參數爲集羣內任意節點,用於發現集羣。cluster-master-id 參數用於指明做爲哪一個主節點的從節點,若是不加這個參數,則自動分配給從節點較少的主節點:

redis-cli --cluster add-node 127.0.0.1:6486 127.0.0.1:6479 --cluster-slave \
--cluster-master-id 819f867afd1da1acfb1a528d3efa91cffb02ba97
複製代碼

3.2 集羣縮容

集羣縮容的命令以下:第一個參數爲集羣內任意節點,用於發現集羣;第二個參數爲須要刪除的節點:

redis-cli --cluster del-node 127.0.0.1:6479 `<node-id>`
複製代碼

須要注意的是待刪除的主節點上必須爲空,若是不爲空則須要將它上面的槽和數據遷移到其餘節點上,和擴容時同樣,可使用 reshard 命令來完成數據遷移。

參考資料

  1. 付磊,張益軍 . 《Redis 開發與運維》. 機械工業出版社 . 2017-3-1
  2. 官方文檔:Redis cluster tutorial

更多文章,歡迎訪問 [全棧工程師手冊] ,GitHub 地址:github.com/heibaiying/…

相關文章
相關標籤/搜索