溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

干貨:一文詳解Redis集群原理核心內(nèi)容

發(fā)布時間:2020-05-21 02:55:54 來源:網(wǎng)絡(luò) 閱讀:1362 作者:Java_老男孩 欄目:編程語言

集群原理

一個系統(tǒng)建立集群主要需要解決兩個問題:數(shù)據(jù)同步問題和集群容錯問題。

Naive方案

一個簡單粗暴的方案是部署多臺一模一樣的Redis服務(wù),再用負載均衡來分?jǐn)倝毫σ约氨O(jiān)控服務(wù)狀態(tài)。這種方案的優(yōu)勢在于容錯簡單,只要有一臺存活,整個集群就仍然可用。但是它的問題在于保證這些Redis服務(wù)的數(shù)據(jù)一致時,會導(dǎo)致大量數(shù)據(jù)同步操作,反而影響性能和穩(wěn)定性。

Redis集群方案

Redis集群方案基于分而治之的思想。Redis中數(shù)據(jù)都是以Key-Value形式存儲的,而不同Key的數(shù)據(jù)之間是相互獨立的。因此可以將Key按照某種規(guī)則劃分成多個分區(qū),將不同分區(qū)的數(shù)據(jù)存放在不同的節(jié)點上。這個方案類似數(shù)據(jù)結(jié)構(gòu)中哈希表的結(jié)構(gòu)。在Redis集群的實現(xiàn)中,使用哈希算法(公式是CRC16(Key) mod 16383)將Key映射到0~16383范圍的整數(shù)。這樣每個整數(shù)對應(yīng)存儲了若干個Key-Value數(shù)據(jù),這樣一個整數(shù)對應(yīng)的抽象存儲稱為一個槽(slot)。每個Redis Cluster的節(jié)點——準(zhǔn)確講是master節(jié)點——負責(zé)一定范圍的槽,所有節(jié)點組成的集群覆蓋了0~16383整個范圍的槽。

據(jù)說任何計算機問題都可以通過增加一個中間層來解決。槽的概念也是這么一層。它介于數(shù)據(jù)和節(jié)點之間,簡化了擴容和收縮操作的難度。數(shù)據(jù)和槽的映射關(guān)系由固定算法完成,不需要維護,節(jié)點只需維護自身和槽的映射關(guān)系。

干貨:一文詳解Redis集群原理核心內(nèi)容

Slave

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

請求重定向

當(dāng)Redis節(jié)點接收到對某個key的命令時,如果這個key對應(yīng)的槽不在自己的負責(zé)范圍內(nèi),則返回MOVED重定向錯誤,通知客戶端到正確的節(jié)點去訪問數(shù)據(jù)。

如果頻繁出現(xiàn)重定向錯誤,勢必會影響訪問的性能。由于從key映射到槽的算法是固定公開的,客戶端可以在內(nèi)部維護槽到節(jié)點的映射關(guān)系,訪問數(shù)據(jù)時可以自己通過key計算出槽,然后找到正確的節(jié)點,減少重定向錯誤。目前大部分開發(fā)語言的Redis客戶端都會實現(xiàn)這個策略。這個地址https://redis.io/clients可以查看主流語言的Redis客戶端。

節(jié)點通信

盡管不同節(jié)點存儲的數(shù)據(jù)相互獨立,這些節(jié)點仍然需要相互通信以同步節(jié)點狀態(tài)信息。Redis集群采用P2P的Gossip協(xié)議,節(jié)點之間不斷地通信交換信息,最終所有節(jié)點的狀態(tài)都會達成一致。常用的Gossip消息有下面幾種:

  • ping消息:每個節(jié)點不斷地向其他節(jié)點發(fā)起ping消息,用于檢測節(jié)點是否在線和交換節(jié)點狀態(tài)信息。
  • pong消息:收到ping、meet消息時的響應(yīng)消息。
  • meet消息:新節(jié)點加入消息。
  • fail消息:節(jié)點下線消息。
  • forget消息:忘記節(jié)點消息,使一個節(jié)點下線。這個命令必須在60秒內(nèi)在所有節(jié)點執(zhí)行,否則超過60秒后該節(jié)點重新參與消息交換。實踐中不建議直接使用forget命令來操作節(jié)點下線。

節(jié)點下線

當(dāng)某個節(jié)點出現(xiàn)問題時,需要一定的傳播時間讓多數(shù)master節(jié)點認(rèn)為該節(jié)點確實不可用,才能標(biāo)記標(biāo)記該節(jié)點真正下線。Redis集群的節(jié)點下線包括兩個環(huán)節(jié):主觀下線(pfail)和客觀下線(fail)。

  • 主觀下線:當(dāng)節(jié)點A在cluster-node-timeout時間內(nèi)和節(jié)點B通信(ping-pong消息)一直失敗,則節(jié)點A認(rèn)為節(jié)點B不可用,標(biāo)記為主觀下線,并將狀態(tài)消息傳播給其他節(jié)點。
  • 客觀下線:當(dāng)一個節(jié)點被集群內(nèi)多數(shù)master節(jié)點標(biāo)記為主觀下線后,則觸發(fā)客觀下線流程,標(biāo)記該節(jié)點真正下線。

故障恢復(fù)

一個持有槽的master節(jié)點客觀下線后,集群會從slave節(jié)點中選出一個提升為master節(jié)點來替換它。Redis集群使用選舉-投票的算法來挑選slave節(jié)點。一個slave節(jié)點必須獲得包括故障的master節(jié)點在內(nèi)的多數(shù)master節(jié)點的投票后才能被提升為master節(jié)點。假設(shè)集群規(guī)模為3主3從,則必須至少有2個主節(jié)點存活才能執(zhí)行故障恢復(fù)。如果部署時將2個主節(jié)點部署到同一臺服務(wù)器上,則該服務(wù)器不幸宕機后集群無法執(zhí)行故障恢復(fù)。

默認(rèn)情況下,Redis集群如果有master節(jié)點不可用,即有一些槽沒有負責(zé)的節(jié)點,則整個集群不可用。也就是說當(dāng)一個master節(jié)點故障,到故障恢復(fù)的這段時間,整個集群都處于不可用的狀態(tài)。這對于一些業(yè)務(wù)來說是不可忍受的??梢?strong>在配置中將cluster-require-full-coverage配置為no,那么master節(jié)點故障時只會影響訪問它負責(zé)的相關(guān)槽的數(shù)據(jù),不影響對其他節(jié)點的訪問。

搭建集群

啟動新節(jié)點

修改Redis配置文件以啟動集群模式:

# 開啟集群模式
cluster-enabled yes
# 節(jié)點超時時間,單位毫秒
cluster-node-timeout 15000
# 集群節(jié)點信息文件
cluster-config-file "nodes-6379.conf"

然后啟動新節(jié)點。

發(fā)送meet消息將節(jié)點組成集群

使用客戶端發(fā)起命令cluster <ip> <port>,節(jié)點會發(fā)送meet消息將指定IP和端口的新節(jié)點加入集群。

干貨:一文詳解Redis集群原理核心內(nèi)容

分配槽

上一步執(zhí)行完后我們得到的是一個還沒有負責(zé)任何槽的“空”集群。為了使集群可用,我們需要將16384個槽都分配到master節(jié)點數(shù)。

在客戶端執(zhí)行cluster add addslots {<a>...<b>}命令,將<a>~<b>范圍的槽都分配給當(dāng)前客戶端所連接的節(jié)點。將所有的槽都分配給master節(jié)點后,執(zhí)行cluster nodes命令,查看各個節(jié)點負責(zé)的槽,以及節(jié)點的ID。

接下來還需要分配slave節(jié)點。使用客戶端連接待分配的slave節(jié)點,執(zhí)行cluster replicate <nodeId>命令,將該節(jié)點分配為<nodeId>指定的master節(jié)點的備份。

使用命令直接創(chuàng)建集群

在Redis 5版本中redis-cli客戶端新增了集群操作命令。

如下所示,直接使用命令創(chuàng)建一個3主3從的集群:

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

如果你用的是舊版本的Redis,可以使用官方提供的redis-trib.rb腳本來創(chuàng)建集群:

./redis-trib.rb create --replicas 1 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

集群伸縮

擴容

擴容操作與創(chuàng)建集群操作類似,不同的在于最后一步是將槽從已有的節(jié)點遷移到新節(jié)點。

  1. 啟動新節(jié)點:同創(chuàng)建集群。
  2. 將新節(jié)點加入到集群:使用redis-cli --cluster add-node命令將新節(jié)點加入集群(內(nèi)部使用meet消息實現(xiàn))。
  3. 遷移槽和數(shù)據(jù):添加新節(jié)點后,需要將一些槽和數(shù)據(jù)從舊節(jié)點遷移到新節(jié)點。使用命令redis-cli --cluster reshard進行槽遷移操作。

收縮

為了安全刪除節(jié)點,Redis集群只能下線沒有負責(zé)槽的節(jié)點。因此如果要下線有負責(zé)槽的master節(jié)點,則需要先將它負責(zé)的槽遷移到其他節(jié)點。

  1. 遷移槽。使用命令redis-cli --cluster reshard將待刪除節(jié)點的槽都遷移到其他節(jié)點。
  2. 忘記節(jié)點。使用命令redis-cli --cluster del-node刪除節(jié)點(內(nèi)部使用forget消息實現(xiàn))。

集群配置工具

如果你的redis-cli版本低于5,那么可以使用redis-trib.rb腳本來完成上面的命令。點擊這里查看redis-cliredis-trib.rb操作集群的命令。

持久化

Redis有RDB和AOF兩種持久化策略。

一個RDB持久化的坑

RDB持久化神坑:

  • 即使設(shè)置了save ""試圖關(guān)閉RDB,然而RDB持久化仍然有可能會觸發(fā)。
  • 從節(jié)點全量復(fù)制(比如新增從節(jié)點時),主節(jié)點觸發(fā)RDB持久化產(chǎn)生RDB文件。然后發(fā)送RDB文件給從節(jié)點。最后該從節(jié)點和對應(yīng)的主節(jié)點都會有RDB文件。
  • 執(zhí)行shutdown時,如果沒有開啟AOF,也會觸發(fā)RDB持久化。
  • 不管save如何設(shè)置,只要RDB文件存在,redis啟動時就會去加載該文件。

后果:

  • 如果關(guān)閉了RDB持久化(以及AOF持久化),那么當(dāng)Redis重啟時,則會加載上一次從節(jié)點全量復(fù)制或者執(zhí)行shutdown時保存的RDB文件。而這個RDB文件很可能是一份過時已久的數(shù)據(jù)。
  • Cluster模式下,Redis重啟并從RDB文件恢復(fù)數(shù)據(jù)后,如果沒有讀取到cluster-config-file中nodes的配置,則標(biāo)記自己為單獨的master并占用從RDB中恢復(fù)的數(shù)據(jù)的Key對應(yīng)的槽,導(dǎo)致此節(jié)點無法再加入其它集群。
向AI問一下細節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI