溫馨提示×

溫馨提示×

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

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

Redis Cluster如何實現(xiàn)寫安全的重要特性

發(fā)布時間:2021-10-28 16:37:47 來源:億速云 閱讀:118 作者:iii 欄目:web開發(fā)

本篇內(nèi)容介紹了“Redis Cluster如何實現(xiàn)寫安全的重要特性”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、接口和架構(gòu)

1、接口

Redis Cluster的接口基本向前兼容,仍然是key-value類型。

2、架構(gòu)

Redis Cluster包含server和client兩個組件。一個Redis  Cluster可以包含多個server,可以包含多個客戶端。每個客戶端可以連接任意的server,讀取寫入數(shù)據(jù)。保存在Redis  Cluster中的數(shù)據(jù)會被分成多份,分散地保存在多個server中,并且每一份數(shù)據(jù)也會保存多個副本。

二、實現(xiàn)

1、節(jié)點

在Redis Cluster中,數(shù)據(jù)會被保存到多個Redis server中,每個Redis  server都是一個獨立的進(jìn)程,具有獨立的IP和Port,也被稱之為一個實例,或者叫做節(jié)點(Node)。Client通過這個IP和Port連接到這個Node。

每個節(jié)點都有個node id,node  id是一個全局唯一的標(biāo)識,它是在集群創(chuàng)建時隨機(jī)生成的。一個節(jié)點的id始終都不會變化的,但是node的IP和port是可以變化的。

2、Node table

一個Redis Cluster包含哪些節(jié)點,也就是這個集群包含哪些節(jié)點的id,也就是集群成員關(guān)系,這個信息被保存在一個表結(jié)構(gòu)中,被稱之為node  table。node table類似于:

Redis Cluster如何實現(xiàn)寫安全的重要特性

node table會在每個節(jié)點上都保存一份,Redis Cluster通過gossip協(xié)議把node table復(fù)制到所有的節(jié)點。后面會繼續(xù)講述node  table的復(fù)制。

3、集群成員關(guān)系變更

當(dāng)添加一個節(jié)點或者刪除一個節(jié)點時,只需要將命令發(fā)給集群中的任意一個節(jié)點,這個節(jié)點會修改本地的node  table,并且這個修改會最終復(fù)制到所有的節(jié)點上去。添加節(jié)點的命令是CLUSTER MEET,刪除節(jié)點的命令是CLUSTER FORGET。

舉例來說明:

  • 打算搭建一個3個master節(jié)點的集群,當(dāng)集群創(chuàng)建以前,所有3個節(jié)點的node table都只包含自己。給其中的一個節(jié)點A發(fā)送命令,CLUSTER  MEET NodeB,節(jié)點A修改自己的node table,將NodeB添加到自己的node table中,并且連接節(jié)點B,把自己的node  table發(fā)送給節(jié)點B,節(jié)點B收到節(jié)點A發(fā)送過來的node table,會更新自己的node table,這時節(jié)點B就知道集群中還有節(jié)點A存在。

這時,給節(jié)點A再發(fā)送CLUSTER MEET NodeC,節(jié)點A會把節(jié)點C添加到自己的node table,并且把自己的node  table復(fù)制給節(jié)點B,節(jié)點B把接收到的node table更新自己的本地的node table,從而知道節(jié)點C的加入。同樣節(jié)點A會把自己的node  table發(fā)給節(jié)點C,節(jié)點C會更新自己本地的node table,從而知道要加入的集群中已經(jīng)存在節(jié)點A和節(jié)點B。

4、槽

前面說過Redis Cluster會把數(shù)據(jù)分成多份,也就是把數(shù)據(jù)進(jìn)行分片。Redis Cluster中的每一份數(shù)據(jù)被稱為槽(Slot)。Redis  Cluster將數(shù)據(jù)拆分成16384份,也就是說有16384個槽。

Redis  Cluster采用哈希(Hash)機(jī)制來拆分?jǐn)?shù)據(jù)。首先,數(shù)據(jù)的key通過CRC16算法計算出一個哈希值。這個哈希值再對16384取余,這個余數(shù)就是槽位,被稱為hash  slot。具體的CRC16算法可以參看Redis官方文檔。所有余數(shù)相同的key都在一個slot中,也就是說,一個slot其實就是一批hash余數(shù)相同的key。

每個hash slot都會保存在Redis Cluster一個節(jié)點中。具體哪個hash  slot被保存在哪個實例中,就形成了類似于一個map的數(shù)據(jù)結(jié)構(gòu),被稱之為hash slot map。hash slot map類似于:

0 -> NodeA 1 -> NodeA 2 -> NodeB ... 16383 -> NodeN

與node table相同,hash slot map也會在每個節(jié)點上都會保存一份,Redis Cluster通過gossip協(xié)議把hash slot  map復(fù)制到所有節(jié)點。同樣,后面還會講述hash slot map的復(fù)制。

《Redis官方文檔》Redis Cluster Specification,  https://redis.io/topics/cluster-spec.

5、數(shù)據(jù)分片變更

要修改數(shù)據(jù)分片關(guān)系,可以連接任意一個節(jié)點,給這個節(jié)點發(fā)送CLUSTER ADDSLOTS, CLUSTER SETSLOT, CLUSTER  DELSLOT命令,修改這個節(jié)點上的hash slot map,該節(jié)點會把這個修改復(fù)制到所有其他節(jié)點,其他節(jié)點會用接收到的hash slot  map更新自己的hash slot map。

CLUSTER ADDSLOTS、CLUSTER DELSLOTS、CLUSTER SETSLOT命令的使用如下:

CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]  CLUSTER DELSLOTS slot1 [slot2] ... [slotN] CLUSTER SETSLOT slot NODE node

CLUSTER ADDSLOTS用來把多個slot分配給當(dāng)前連接的節(jié)點。例如,連接到節(jié)點A執(zhí)行:

CLUSTER ADDSLOTS 1 2 3

這個命令會把slot1、slot2、slot3分配給節(jié)點A。

CLUSTER DELSLOTS用來把多個slot從當(dāng)前連接的節(jié)點刪除。例如,連接到節(jié)點A執(zhí)行:

CLUSTER DELSLOTS 1 2 3

這個命令會把slot1、slot2、slot3從節(jié)點A上刪除。

CLUSTER  SETSLOT用來把一個slot分配給指定的節(jié)點,可以不是當(dāng)前連接的節(jié)點,另外這個命令還可以設(shè)定MIGRATING和IMPORTING兩個狀態(tài),我們后面再講。例如,連接到節(jié)點A執(zhí)行:

CLUSTER SETSLOT 1 nodeB

這個命令會把slot1分配給節(jié)點B。

6、Slave

一個slot會被保存多個副本,既一個slot會保存在多個節(jié)點上,也就是slot會復(fù)制到多個節(jié)點上。Redis  Cluster的復(fù)制是以節(jié)點為單位的,一個節(jié)點上的所有slot會采用相同的復(fù)制。

具體來說就是,其中一個節(jié)點會負(fù)責(zé)處理這個節(jié)點上所有slot的寫操作,這個節(jié)點被稱為master,而其余的節(jié)點被稱為slave節(jié)點。一個master可以有多個slave。在同一個節(jié)點上的所有slot的所有的寫操作都會被從master節(jié)點異步復(fù)制到所有的slave節(jié)點。所以slave會具有與master相同的slot。

通過SLAVEOF命令來設(shè)置slave節(jié)點。SLAVEOF命令用來改變一個slave節(jié)點的復(fù)制設(shè)置。SLAVEOF命令有兩種格式:

  • SLAVEOF NO ONE

  • SLAVEOF host port

具體來講,SLAVEOF NO ONE命令會停止一個slave節(jié)點的復(fù)制,并且把這個slave節(jié)點變成master節(jié)點。SLAVEOF host  port命令會停止一個slave節(jié)點的復(fù)制,丟棄數(shù)據(jù)集,開始從host和port指定的新master節(jié)點復(fù)制。

master和slave的關(guān)系會被記錄在hash slot  table中,相當(dāng)于一個slot會映射到多個節(jié)點上,其中一個節(jié)點是master,其他記錄的節(jié)點是slave。

加入了master/slave信息后的hash slot map類似于:

0 -> NodeA,NadeA1(slave) 1 -> NodeA,NadeA1(slave) 2 -> NodeB,NadeB1(slave) ... 16383 -> NodeN,NadeN1(slave)

作為hash slot map的一部分,master/slave信息也會通過gossip協(xié)議復(fù)制到集群中的每個節(jié)點。

7、Configuration

node table和hash slot map這兩個信息,被稱之為Configuration,在其他的分布式系統(tǒng)中,也被稱之為元數(shù)據(jù)。

前面我們講過,hash slot map和node  table在每個節(jié)點都會保存一份,并且對這兩個信息的任何改動,都會通過gossip協(xié)議傳播(propogate)到所有的節(jié)點。本文我們就不繼續(xù)展開gossip協(xié)議了,對Redis  Cluster的gossip協(xié)議的實現(xiàn)感興趣的同學(xué)可以參看redis的文檔。

在某些分布式系統(tǒng)中,元數(shù)據(jù)被存儲在一個單獨的架構(gòu)組件中的。Redis  Cluster并沒有這樣一個元數(shù)據(jù)存儲的組件,而是把元數(shù)據(jù)分散的存儲在所有的節(jié)點上。

hash slot map和node  table在集群創(chuàng)建時會被創(chuàng)建出來,并且會隨著后續(xù)的集群變更(比如failover和擴(kuò)容、縮容等運維操作,后面會講述)而跟隨著變更。變動會在一個Node上發(fā)起,通過gossip協(xié)議傳播到所有其他的節(jié)點。這兩個信息在所有節(jié)點都會保存,并且會最終達(dá)到一致。

8、集群創(chuàng)建

創(chuàng)建Redis Cluster時,首先要以cluster模式運行多個redis server,redis  server運行起來后,這些server的node id就已經(jīng)生成了。但是這些redis  server并沒有形成集群,也就是server彼此之間并不知道相互的存在。接下來運行CLUSTER  MEET命令,讓這些節(jié)點形成一個集群。但是這時集群仍然沒有處于運行狀態(tài),需要分配slot,通過CLUSTER  SLOTADD命令把slot分配給具體的節(jié)點之后,集群就可以處理client的命令了。

9、客戶端的讀寫操作

客戶端要讀取、寫入數(shù)據(jù)時,雖然client可以連接任意server,但是實際中,client需要根據(jù)實際需求連接到server讀取、寫入數(shù)據(jù)。client需要先根據(jù)key計算出hash  slot,連接到負(fù)責(zé)這個hash slot的節(jié)點進(jìn)行讀寫操作。這樣的話,client就要需要知道hash slot ->  node的映射關(guān)系,也就是需要知道hash slot map。

前面講過hash slot map被保存在server端的每個節(jié)點上。client可以從任意節(jié)點獲取hash slot  map,并且把它緩存到client本地,下次操作時根據(jù)本地緩存直接進(jìn)行操作,但是需要處理緩存信息過期的問題,如果client發(fā)現(xiàn)hash slot  map發(fā)生變化(即client讀取寫入數(shù)據(jù)時server回錯誤,接下來會詳細(xì)講述),會重新從server端獲取新的hash slot map。通過hash  slot map可以判斷某個key應(yīng)該存在在哪個節(jié)點上,client再連接這個節(jié)點進(jìn)行讀寫操作。

10、MOVED Redirection

hash slot map會發(fā)生變更,這些變更會復(fù)制到所有的節(jié)點,但是gossip保證的是最終復(fù)制到所有的節(jié)點,再加上client會緩存hash slot  map,client可能會把某個key的請求發(fā)給錯誤的節(jié)點來處理。

錯誤的節(jié)點收到請求后,發(fā)現(xiàn)這個key不應(yīng)該自己來處理,會給客戶端返回MOVED的錯誤,在錯誤消息中,會告訴客戶端,哪個節(jié)點應(yīng)該負(fù)責(zé)這個slot。Client收到MOVED消息后,會向消息中指定的節(jié)點再次發(fā)送請求。

client可以將slot的節(jié)點信息更新到本地緩存的hash slot map中,但是更好的方法是,重新獲取完整的hash slot  map,替換本地的緩存。因為在大多數(shù)情況下,hash slot map中的變更不僅僅只修改一個slot。

雖然client按照MOVED消息中的節(jié)點信息重新發(fā)送請求,但是client仍然可能再次從新節(jié)點收到MOVED錯誤消息,因為上一個節(jié)點的hash slot  map可能也不是最新的。但是因為hash slot map最終會在所有的節(jié)點上一致,所以client在幾次收到MOVED錯誤后,最終會獲取到最新的hash  slot map。

11、Failover、currentEpoch、lastVoteEpoch

當(dāng)master發(fā)生故障宕機(jī)后,Redis Cluster會選出一個slave來接替這個master。

如果有多個slave存在,那么每個slave都可能都會發(fā)現(xiàn)master發(fā)生了宕機(jī),并且試圖把自己變成為master,如果有多個slave成為master,那么這些新master都會更新本地hash  slot map,把舊master負(fù)責(zé)的slot更新成自己,并且把自己對hash slot map的更新傳播給其他的節(jié)點。這會導(dǎo)致hash slot  map在節(jié)點間出現(xiàn)差異。

從而導(dǎo)致,因為所連接的節(jié)點不同,client拿到不同的hash slot  map,對于同一個slot,不同的client會連接不同的節(jié)點,最終導(dǎo)致節(jié)點上的數(shù)據(jù)出現(xiàn)差異。所以failover要保證,只有一個slave被選成新master。

Redis  Cluster采用了類似Raft算法的技術(shù)來防止多個slave被選成master。每個節(jié)點都會有叫做currentEpoch、lastVoteEpoch的兩個值。在集群剛創(chuàng)建時,每個節(jié)點的currentEpoch都是0。

當(dāng)slave發(fā)現(xiàn)master宕機(jī)時,這個slave會增加currentEpoch(即currentEpoch++)。并且向所有的master發(fā)送FAILOVER_AUTH_REQUEST請求,請求中會攜帶自己currentEpoch,master收到FAILOVER_AUTH_REQUEST,如果請求中的currentEpoch比自己的currentEpoch和lastVoteEpoch都大,則記錄請求中的currentEpoch值到自己的currentEpoch、lastVoteEpoch中,并且回復(fù)FAILOVER_AUTH_ACK給slave,回復(fù)中攜帶master的currentEpoch。

所以可以看出,F(xiàn)AILOVER_AUTH_ACK中的Epoch一定與slave的currentEpoch相同。Slave從大多數(shù)的master收到FAILOVER_AUTH_ACK后,則成為master。

上面的過程保證只有一個slave被選出,我們來舉例說明。五個master的集群,master節(jié)點分別是A、B、C、D、E,A節(jié)點有兩個slave,分別是A1和A2。A節(jié)點發(fā)生宕機(jī),A1增加自己的currentEpoch=5(4+1),A1給所有的master發(fā)送FAILOVER_AUTH_REQUEST,節(jié)點B、C、D收到FAILOVER_AUTH_REQUEST,把自己的currentEpoch和lastVoteEpoch更新成5,并且給A1回復(fù)FAILOVER_AUTH_ACK,A1贏得選舉,成為新的master。但是與此同時,A2也發(fā)現(xiàn)A宕機(jī),也試圖選舉成master。A2增加自己的currentEpoch=5(4+1),A2給所有的master發(fā)送FAILOVER_AUTH_REQUEST,但這時的B、C、D的lastVoteEpoch已經(jīng)是5,所以B、C、D不會給A2回復(fù),E還沒有收到A1的請求,所以只有E會給A2回復(fù),但是不能形成大多數(shù),所以A2不能稱為master。

上面講述的過程已經(jīng)可以保證只有一個master被選出,但是除此之外,Redis  Cluster還做了一個優(yōu)化,那就是master回復(fù)了一個請求后不會在給這個master的其他的slave發(fā)送回復(fù)。

12、Configuration epoch

failover完成后,新master會修改hash slot map,把相應(yīng)的slot記錄的節(jié)點改成自己,并且把這次對hash slot  map的改動傳播給其他節(jié)點。

雖然currentEpoch和lastVoteEpoch能保證每次failover只能有一個節(jié)點被選成新的master,但是先后兩次failover,可能選出的兩個不同的master,但是他們對hash  slot map的修改的傳播卻是異步,也就是后面一次failover的改動可能先于第一次failover的改動到達(dá)某個節(jié)點,從而導(dǎo)致節(jié)點間對hash slot  map這個信息產(chǎn)生不一致。

Redis Cluster通過configEpoch來解決這個問題。每個節(jié)點會保存一個configEpoch的值。相當(dāng)于在node  table中還會有一列數(shù)據(jù)叫configEpoch,類似于下面的表:

Redis Cluster如何實現(xiàn)寫安全的重要特性

每次failover完成后,新選出的master會用currentEpoch覆蓋configEpoch。Failover的機(jī)制保證兩次failover的新master一定是具有不同的currentEpoch,并且后一次的failover的currentEpoch一定比前一次大。這樣就可以保證,即便采用了gossip這樣傳播協(xié)議,仍然能夠保證最后一次failover的hash  slot map的變更會生效,也就是configEpoch更大的變更會生效,并且最終所有的節(jié)點上的hash slot map是一致的。

Slave節(jié)點的configEpoch就是其master的configEpoch。

由于gossip保證的hash slot map最終保持一致的,所以可能存在slave的hash slot  map舊于master,failover不能基于舊的hash slot map基礎(chǔ)上做變更,所以前面failover的過程中還需要補充一個規(guī)則要遵守:

  • 在FAILOVER_AUTH_REQUEST中會攜帶slave節(jié)點的configEpoch,如果這個slave的configEpoch比這個slave負(fù)責(zé)的所有slot的master的configEpoch中任意一個要小,則master不會給slave回復(fù)FAILOVER_AUTH_ACK。

13、Resharding

在集群創(chuàng)建之后,我們還會有對Redis cluster做擴(kuò)容、縮容、balancing這樣的運維需求,這些需求本質(zhì)上都可以用 Resharding  操作解決,resharding操作就是把slot在節(jié)點間重新的分布,把slot從一個節(jié)點轉(zhuǎn)移到另外一個節(jié)點上。在Redis  Cluster中,擴(kuò)容需求實質(zhì)上就是加入一個新的節(jié)點,再把一些slot分配到這個新節(jié)點上。

縮容需求實質(zhì)上就是先把這個節(jié)點上的所有slot分配到其他節(jié)點上,再把這個節(jié)點從集群中移出。當(dāng)節(jié)點間的流量不均衡時,我們有balancing這樣的需求,balancing就是把流量比較大的節(jié)點上的一些slot分配都流量比較少的節(jié)點上。

Resharding操作可以是對整個hash slot  map的調(diào)整,也就是可以包括對多個slot的遷移(migration),遷移就是把一個slot從一個節(jié)點遷移到另外一個節(jié)點。一個slot  migration操作包括前面講的hash slot  map變更,另外還包括key的遷移操作。要把一個slot遷移到另外的節(jié)點上,首先把這個slot上的所有的key遷移到這個節(jié)點,當(dāng)把所有key都遷移完后,再進(jìn)行hash  slot map變更,當(dāng)hash slot map變更完成,這次slot migration結(jié)束。

Redis Cluster使用CLUSTER  SETSLOT來設(shè)置遷移。舉例說明,將slot1從節(jié)點A遷移到節(jié)點B。分別對節(jié)點A和節(jié)點B執(zhí)行下面的命令:

  • 節(jié)點A上:CLUSTER SETSLOT 1 MIGRATING NODEB

  • 節(jié)點B上:CLUSTER SETSLOT 1 IMPORTING NODEA

其中,MIGRATING表示數(shù)據(jù)要從這個節(jié)點遷出,而IMPORTING表示數(shù)據(jù)要往這個節(jié)點遷入。

執(zhí)行完這兩個命令后,節(jié)點A中的slot1不在創(chuàng)建新的key。一個叫做redis-trib的特殊的程序負(fù)責(zé)把所有的key從節(jié)點A遷移到節(jié)點B。redis-trib會執(zhí)行下面的命令:

CLUSTER GETKEYSINSLOT slot count

這個命令會返回count個key,對于每個返回的key,redis-trib執(zhí)行下面的命令:

MIGRATE target_host target_port key target_database id timeout

這個命令會原子地把一個key從節(jié)點A遷移到節(jié)點B。具體來說,MIGRATE命令會連接目標(biāo)節(jié)點,并發(fā)向目標(biāo)節(jié)點發(fā)送這個key,一旦目標(biāo)節(jié)點收到這個key,則從自己的數(shù)據(jù)庫中刪除這個key,在這個過程中,節(jié)點A和節(jié)點B都會加鎖。

在把所有的key遷移完后,再分別在兩個節(jié)點上執(zhí)行下面的命令:

CLUSTER SETSLOT slot NODE nodeA

把所有的key遷移完一般需要一些時間,也就是說在開始遷移后和完成遷移前,在這個窗口期內(nèi),key的實際的分布,與hash slot  map里記錄的是不一致的,client按照hash slot map訪問key,會出現(xiàn)錯誤。

Redis Cluster通過ASK redirection來解決這個問題。按照client端的hash slot  map,slot1的key一定會發(fā)給節(jié)點A,節(jié)點A收到這個請求后,如果發(fā)現(xiàn)這個key已經(jīng)遷移到節(jié)點B了,那么就會給client回復(fù)ASK  redirection,client收到ASK redirection后,會向節(jié)點b先發(fā)送一個ASKING命令,之后在發(fā)送對這個key的請求。

14、Configuration的實際存儲

Hash slot map和node table都是邏輯上的結(jié)構(gòu),他們在Redis  Cluster中的實際存儲結(jié)構(gòu)稍有不同(詳情看結(jié)尾參考資料1、2、3、4)。

在節(jié)點的內(nèi)存中,用兩個變量來存儲這兩個信息:

  • myself變量:myself代表本節(jié)點,是一個ClusterNode類型的變量,這個變量中,包含本節(jié)點的configEpoch,還包括slaveof,如果是slave節(jié)點則在slaveof中記錄著它的master節(jié)點,還包括一個bitmap,代表這個節(jié)點負(fù)責(zé)的所有的slot的槽位值。這個bitmap有2048個byte組成,一總是16384(2048*8)個bit,每個bit代表一個slot,bit置1,代表這個節(jié)點負(fù)責(zé)這個slot;

  • cluster變量:代表了所在集群的狀態(tài),它包含currentEpoch、lastVoteEpoch和slots數(shù)組,slots數(shù)組的index代表了slot,數(shù)組的每個成員都指向一個節(jié)點,是一個ClusterNode類型的變量,與myself變量的類型一樣。

所有Configuration的更改都會被保存到磁盤中,具體來講是保存到一個名字叫node.conf的文件中,這個文件是Redis  Cluster負(fù)責(zé)寫入的,不需要人工配置。

node.conf按照節(jié)點維度進(jìn)行保存。每一行對應(yīng)一個節(jié)點,每行分別包含這些信息:id,ip:port,flag,slaveof,ping  timestamp, pong timespamp,configEpoch,link status,slots。

所有的節(jié)點結(jié)束后,會在文件的最后保存curruntEpoch和lastVoteEpoch兩個變量。其中flag字段是枚舉類型,會指明這個節(jié)點是不是自己,節(jié)點類型是master還是slave。

如果是slave節(jié)點,則會在slaveof字段記錄其master節(jié)點的id。如果是master節(jié)點,則在最后多一個slots字段,記錄著這個節(jié)點負(fù)責(zé)著哪些slot。Flags字段還記錄著其他非常重

要的狀態(tài),本文就不繼續(xù)展開了。

同樣,ping timestamp、pong timestmap、link staus三個字段本文也不繼續(xù)展開了。

具體的node.conf文件類似下面的例子:

[root@10.112.178.141 data]# cat nodes-6384.conf fb763117270d14205c41174605b15741co03a945 10.112.178.174:6383 slave 5e35bda1a44c8d781eb54e08be88a3bab42070f3 0 1596683852819 2 connected 3dc5890fb1591e3b20196f81eb5f2f99754253e8 10.112.178.141:6383 master - 0 1596683851915 1 connected 0-5461 f1967b687c9b2c27108cce08517e98e7a80d5e7e 10.112.178.171:6383 slave 3dc5890fb1591e3b20196f81eb5f2f99754253e8 0 1596683850813 1 connected 2bbab7353e973e991566df3bb52afb4857a7bf25 10.112.178.171:6384 slave 1f0a8cf1bfd0c915ef404482f3dc6bf5c7cf41f5 0 1596683848812 3 connected 5e35bda1a44c8d781eb54e08be88a3bab42070f3 10.112.178.142:6383 master - 0 1596683849813 2 connected 5462-10923 1f0a8cf1bfd0c915ef404482f3dc6bf5c7cf41f5 10.112.178.141:6384 myself,master - 0 0 3 connected 10924-16383

節(jié)點啟動時會讀取node.conf文件,把里面的信息加載到myself和cluster兩個變量中。Slot信息會被轉(zhuǎn)換成bitmap保存在myself變量中。并且slot信息還會逆向的轉(zhuǎn)換成slot到節(jié)點的映射保存在cluster變量中。

hash slot map變更或者node  table變更,就是修改內(nèi)存中的myself變量和cluater變量,并且每次變更都會把這兩個變量序列化轉(zhuǎn)化后保存到node.conf中。

15、查看configuration

Redis Cluster提供了兩個命令來查看configuration:

第一個是CLUSTER SLOT命令,用來展示hash slot維度的信息,CLUSTER SLOT命令的展示如下:

127.0.0.1:7000> cluster slots 1)  1) (integer) 5461 2) (integer) 10922 3)  1) "127.0.0.1" 2) (integer) 7001 4)  1) "127.0.0.1" 2) (integer) 7004 2)  1) (integer) 0 2) (integer) 5460 3)  1) "127.0.0.1" 2) (integer) 7000 4)  1) "127.0.0.1" 2) (integer) 7003 3)  1) (integer) 10923 2) (integer) 16383 3)  1) "127.0.0.1" 2) (integer) 7002 4)  1) "127.0.0.1" 2) (integer) 7005

第二個是CLUSTER NODE命令,用來展示node table維度的信息,CLUSTER NODE命令的展示如下:

$ redis-cli cluster nodes d1861060fe6a534d42d8a19aeb36600e18785e04 127.0.0.1:6379 myself - 0 1318428930 1 connected 0-1364 3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 2 connected 1365-2729 d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 3 connected 2730-4095

CLUSTER NODE、CLUSTER  SLOT兩個命令可以連接到任意節(jié)點上執(zhí)行,這兩個命令都是讀取的這個節(jié)點的本地信息,根據(jù)gossip的特性,存在這兩個命令展示的不是最新的configuration的可能性。

16、Conflict

雖然前面講的failover過程通過大多數(shù)master投票的方式保證只有一個slave選中,并且產(chǎn)生唯一的configEpoch。但是Resharding的過程卻沒有經(jīng)過大多數(shù)的master的投票。

執(zhí)行slot遷移時,僅僅是在集群中所有configEpoch中最大的那個configEpoch的基礎(chǔ)上,再加一而得到的。并且由于Resharding一般包括多個slot的遷移,Redis  cluster目前的做法是,在一次resharding過程中,所有的slot遷移使用的configEpoch都是第一個slot遷移時產(chǎn)生的那個configEpoch。

而failover和resharding都會修改hash slot  map,如果在resharding的過程中發(fā)生了failover,這就有可能導(dǎo)致對hash slot  map的修改產(chǎn)生沖突。另外,手動failover也是不經(jīng)過master投票的,也就是執(zhí)行CLUSTER FAILOVER命令(帶TAKEOVER參數(shù))。

產(chǎn)生沖突就是指針對同一個slot,slot被修改成映射到不同的節(jié)點上,并且這些修改具有相同的configEpoch。

為了解決這個問題,Redis  cluster需要存在一個沖突解決的機(jī)制。如果一個master發(fā)現(xiàn)相同的configEpoch,則比較一下兩個節(jié)點的id,id小的節(jié)點,把自己currentEpoch加一,作為自己的configEpoch。

三、Write Safety

由于有沖突的存在,可能導(dǎo)致不同的節(jié)點上的hash slot  map不一致,取決于連接的節(jié)點不同,一部分client可能會把某個slot的key寫入到一個節(jié)點中,而另外一部分client會把同樣slot的key寫入到另外一個節(jié)點中。當(dāng)沖突被解決后,其中一個節(jié)點上接受的寫入會丟失。

另外,由于master和slave之間的數(shù)據(jù)復(fù)制是異步的,在failover時如果slave還沒有收到最新的數(shù)據(jù),就發(fā)生了failover,那么這部分寫入就會丟失。Redis  cluster在這方面做了一個優(yōu)化,當(dāng)一個slave發(fā)現(xiàn)master發(fā)生了宕機(jī),它不會立即開始選舉的過程,它會等待一個時間,這個時間計算公式如下:

DELAY = 500 milliseconds + random delay between 0 and 500 milliseconds + SLAVE_RANK * 1000 milliseconds

這個計算公式中,

  • 第一部分是一個固定500毫秒的時間,這是為了給master充分的時間,也發(fā)現(xiàn)節(jié)點宕機(jī)這個事實;

  • 第二個是隨機(jī)等待一段時間,這是為了盡量避免多個slave同時發(fā)現(xiàn)master宕機(jī),然后同時開始選舉,導(dǎo)致master被瓜分,從而導(dǎo)致所有的選舉都不成功;

  • 第三部分是slave的rank,rank主要取決于slave的復(fù)制進(jìn)度,復(fù)制的數(shù)據(jù)越多,則rank越小,也就是越短的等待時間,越先開始選舉,有更大的可能性被選成新的master。但這僅僅是個優(yōu)化,不能完全防止丟數(shù)據(jù)的可能。

“Redis Cluster如何實現(xiàn)寫安全的重要特性”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

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

AI