在Redis集羣(一):集羣搭建中解了對Redis集羣的基本操做,實際生產應用中確保集羣的穩定可用也很是重要。html
Redis3.0版本以前沒有提供集羣功能,通常用一致性Hash和Hash環在客戶端作key的分片。3.0版本開始Redis使用Hash槽實現分片,Redis共準備了16384個槽(slot),這些slot分佈在集羣節點上。node
爲何是16384?redis
Redis須要維護節點和槽之間的映射關係,每一個節點須要知道本身有哪些槽,而且須要在結點之間傳遞這個消息。
爲了節省存儲空間,每一個節點用一個Bitmap來存放其對應的槽:
2k = 2*1024*8 = 16384,也就是說,每一個結點用2k的內存空間,總共16384個比特位,就能夠存儲該結點對應了哪些槽。而後這2k的信息,經過Gossip協議,在結點之間傳遞。算法
客戶端使用公式(HASH_SLOT = CRC16(key) mod 16384)計算出Key對應的slot位置,根據從服務端獲取slot和節點映射信息,找到slot對應的節點。緩存
CRC16算法實現參考:http://redisdoc.com/topic/cluster-spec.html#a-crc16-ansibash
若是服務端slot對應的節點發生了變動,客戶端向舊節點發送操做時會收到MOVED指令:(error) MOVED 866 127.0.0.1:7000,意思是866這個slot已經遷移到127.0.0.1:7000節點,客戶端向新節點發送操做並更新本地緩存的slot和節點映射信息。服務器
在Redis集羣(一):集羣搭建中瞭解到,當Redis部分slot所在的節點不可用時,整個集羣都變的不可用,因此集羣容災最重要的就是要避免這種狀況出現。佈局
優勢:當master宕機時,slave自動提高爲master節點恢復服務。測試
缺點:若是slave節點也宕機,因爲沒有其餘從節點能夠提高爲主節點(由於master節點仍未恢復正常),集羣無法繼續進行正常操做。若是爲每一個master節點都配置多個slave節點,則會形成浪費。spa
不對稱性集羣是讓集羣佈局時不時地自動變化。
例如,假設集羣有三個主節點 A,B,C。節點 A 和 B 都各有一個從節點,A1 和 B1。節點 C 有兩個從節點:C1 和 C2。
遷移算法不用任何形式的協議,由於 Redis 集羣中的從節點佈局不是集羣配置信息(配置信息要求先後一致而且/或者用 config epochs 來標記版本號)的一部分。 它使用的是一個避免在主節點沒有備份時從節點大批遷移的算法。這個算法保證,一旦集羣配置信息穩定下來,最終每一個主節點都至少會有一個從節點做爲備份。
接下來說這個算法是如何工做的。在開始以前咱們須要定義清楚在這個上下文中什麼纔算是一個好的從節點:一個好的從節點是指從給定節點的角度看,該從節點不處於 FAIL 狀態。
每一個從節點若檢測出存在至少一個沒有好的從節點的單一主節點,那麼就會觸發這個算法的執行。然而在全部檢測出這種狀況的從節點中,只有一部分從節點會採起行動。 一般這「一部分從節點」都只有一個,除非有不一樣的從節點在給定時間間隔裏對其餘節點的失效狀態有稍微不一樣的視角。
採起行動的從節點是屬於那些擁有最多從節點的主節點,而且不處於 FAIL 狀態及擁有最小的節點 ID。
例如,若是有 10 個主節點,它們各有 1 個從節點,另外還有 2 個主節點,它們各有 5 個從節點。會嘗試遷移的從節點是在那 2 個擁有 5 個從節點的主節點中的全部從節點裏,節點 ID 最小的那個。已知不須要用到任何協議,在集羣配置信息不穩定的狀況下,有可能發生一種競爭狀況:多個從節點都認爲本身是不處於 FAIL 狀態而且擁有較小節點 ID(實際上這是一種比較難出現的情況)。若是這種狀況發生的話,結果是多個從節點都會遷移到同個主節點下,不過這種結局是無害的。這種競爭發生的話,有時候會使得割讓出從節點的主節點變成沒有任何備份節點,當集羣再次達到穩定狀態的時候,本算法會再次執行,而後把從節點遷移回它原來的主節點。
最終每一個主節點都會至少有一個從節點做爲備份節點。一般表現出來的行爲是,一個從節點從一個擁有多個從節點的主節點遷移到一個孤立的主節點。
這個算法能經過一個用戶可配置的參數 cluster-migration-barrier 進行控制。這個參數表示的是,一個主節點在擁有多少個好的從節點的時候就要割讓一個從節點出來。例如這個參數若被設爲 2,那麼只有當一個主節點擁有 2 個可工做的從節點時,它的一個從節點會嘗試遷移。
用於測試的集羣:主節點7000/7001分別有從節點7005/7003,主節點7002有從節點7004和7006。
127.0.0.1:7000> cluster nodes 9b022d79cf860c87dc2190cdffc55b282dd60e42 127.0.0.1:7002@17002 master - 0 1542960331000 3 connected 10923-16383 3bcdfbed858bbdd92dd760632b9cb4c649947fed 127.0.0.1:7000@17000 myself,master - 0 1542960330000 1 connected 0-5460 ea4e0dcf8dbf6d4611659b5abbd6563926224f0f 127.0.0.1:7004@17004 slave 9b022d79cf860c87dc2190cdffc55b282dd60e42 0 1542960329000 5 connected 6e41c9b986e863943c1fd41de3411241e0fcb65b 127.0.0.1:7006@17006 slave 9b022d79cf860c87dc2190cdffc55b282dd60e42 0 1542960331690 3 connected e852e07181f20dd960407e5b08f7122870f67c89 127.0.0.1:7003@17003 slave 2a8f29e22ec38f56e062f588e5941da24a2bafa0 0 1542960331000 2 connected 2a8f29e22ec38f56e062f588e5941da24a2bafa0 127.0.0.1:7001@17001 master - 0 1542960328000 2 connected 5461-10922 583a6b71dcde83ab2685fcfd4ea94ecf9e353a1e 127.0.0.1:7005@17005 slave 3bcdfbed858bbdd92dd760632b9cb4c649947fed 0 1542960329000 7 connected
停掉7000後,7005升級爲master。
127.0.0.1:7001> cluster nodes ea4e0dcf8dbf6d4611659b5abbd6563926224f0f 127.0.0.1:7004@17004 slave 9b022d79cf860c87dc2190cdffc55b282dd60e42 0 1542961385025 3 connected 2a8f29e22ec38f56e062f588e5941da24a2bafa0 127.0.0.1:7001@17001 myself,master - 0 1542961380000 2 connected 5461-10922 e852e07181f20dd960407e5b08f7122870f67c89 127.0.0.1:7003@17003 slave 2a8f29e22ec38f56e062f588e5941da24a2bafa0 0 1542961383000 6 connected 583a6b71dcde83ab2685fcfd4ea94ecf9e353a1e 127.0.0.1:7005@17005 master - 0 1542961383995 8 connected 0-5460 9b022d79cf860c87dc2190cdffc55b282dd60e42 127.0.0.1:7002@17002 master - 0 1542961382000 3 connected 10923-16383 6e41c9b986e863943c1fd41de3411241e0fcb65b 127.0.0.1:7006@17006 slave 9b022d79cf860c87dc2190cdffc55b282dd60e42 0 1542961381936 3 connected 3bcdfbed858bbdd92dd760632b9cb4c649947fed 127.0.0.1:7000@17000 master,fail - 1542961362723 1542961362313 1 disconnected
停掉7005後,原屬於7002的從節點7006被提高爲master。
127.0.0.1:7001> cluster nodes ea4e0dcf8dbf6d4611659b5abbd6563926224f0f 127.0.0.1:7004@17004 slave 9b022d79cf860c87dc2190cdffc55b282dd60e42 0 1542961749007 3 connected 2a8f29e22ec38f56e062f588e5941da24a2bafa0 127.0.0.1:7001@17001 myself,master - 0 1542961747000 2 connected 5461-10922 e852e07181f20dd960407e5b08f7122870f67c89 127.0.0.1:7003@17003 slave 2a8f29e22ec38f56e062f588e5941da24a2bafa0 0 1542961750033 6 connected 583a6b71dcde83ab2685fcfd4ea94ecf9e353a1e 127.0.0.1:7005@17005 master,fail - 1542961727108 1542961726285 8 disconnected 9b022d79cf860c87dc2190cdffc55b282dd60e42 127.0.0.1:7002@17002 master - 0 1542961746000 3 connected 10923-16383 6e41c9b986e863943c1fd41de3411241e0fcb65b 127.0.0.1:7006@17006 master - 0 1542961749000 9 connected 0-5460 3bcdfbed858bbdd92dd760632b9cb4c649947fed 127.0.0.1:7000@17000 master,fail - 1542961362723 1542961362313 1 disconnected
停掉7006後,沒有其餘從節點被提高,集羣癱瘓。將7002的cluster-migration-barrier設置爲0,7002的從節點也沒有被提高。可能Redis要求從節點少於2個不給提高?
127.0.0.1:7001> cluster nodes ea4e0dcf8dbf6d4611659b5abbd6563926224f0f 127.0.0.1:7004@17004 slave 9b022d79cf860c87dc2190cdffc55b282dd60e42 0 1542963389063 3 connected 2a8f29e22ec38f56e062f588e5941da24a2bafa0 127.0.0.1:7001@17001 myself,master - 0 1542963389000 2 connected 5461-10922 e852e07181f20dd960407e5b08f7122870f67c89 127.0.0.1:7003@17003 slave 2a8f29e22ec38f56e062f588e5941da24a2bafa0 0 1542963387000 6 connected 583a6b71dcde83ab2685fcfd4ea94ecf9e353a1e 127.0.0.1:7005@17005 master,fail - 1542961727108 1542961726285 8 disconnected 9b022d79cf860c87dc2190cdffc55b282dd60e42 127.0.0.1:7002@17002 master - 0 1542963388024 3 connected 10923-16383 6e41c9b986e863943c1fd41de3411241e0fcb65b 127.0.0.1:7006@17006 master,fail - 1542963208985 1542963206916 9 disconnected 0-5460 3bcdfbed858bbdd92dd760632b9cb4c649947fed 127.0.0.1:7000@17000 master,fail - 1542961362723 1542961362313 1 disconnected
假如咱們某個主節點宕機後,因爲某種原力死活啓動不起來或者沒法接入集羣,須要咱們在新的機器上恢復該節點和數據。
假設7006主節點掛了,複製7006整個目錄到新機器,編輯根目錄下的nodes-7006.conf,將裏面7006的IP改成新機器的IP,直接啓動便可恢復集羣和數據。
若是是在同一臺機器測試,將nodes-7006.conf改爲nodes-7007.conf,修改redis.conf中的7006爲7007。
注意:用7007代替7006後,從新啓動7006時,須要在7006執行flushall和cluster reset,清除以前的集羣信息,而後從新手動將7006加入集羣,不然集羣會報錯。
這種狀況數據確定是沒了,除非以前有作過備份。咱們先想辦法保證恢復集羣可用性。
將當前集羣中任意一臺可用服務的nodes-port.conf文件複製到新機器的Redis目錄下,這裏假設用7008來恢復7007,修改文件名爲nodes-7008.conf,修改redis.conf中相關端口爲7008。
打開nodes-7008.conf文件