集羣:Cluster

Codis算是第三方的解決方案,而Cluster是Redis官方的解決方案。

問題1:Cluster大致工作流程是什麼?

答:Cluster一共有16384個槽位,這些槽位被分配給不同的Redis實例,且Redis實例自己保存16384個槽位映射數據。

當客戶端連接Cluster時,客戶端會得到16384個槽位配置信息,因此客戶端根據配置信息直接從目標Redis查找key信息。

注意,這裏Cluster下的每個Redis節點會將整個集羣的配置信息持久化到配置文件中,所以必須確保配置文件是可寫的,而且儘量不要依靠人工修改配置文件,這跟Codis依靠分佈式數據庫是不一樣的。

問題2:假如客戶端向某個Redis節點發送不屬於其管轄內容的key時會發生什麼?

答:鑑於客戶端已經拿到16384個槽位的映射關係,因此該節點會發現指令的key所在的槽位並不歸自己管理,這時它會向客戶端發送一個特殊的跳轉指令攜帶目標操作的節點地址,告訴客戶端去連這個節點去獲取數據。

-MOVED     key對應的槽位編號    目標節點地址:6381

客戶端收到MOVED指令後,要立即糾正本地的槽位映射表。後續所有key將使用新的槽位映射表。

問題3:能人工分配槽位映射關係嗎?

答:redis-trib。

問題4:redis-trib大致工作內容是什麼?

答:redis-trib遷移的是槽位,當一個槽正在遷移時這個槽就處於中間過渡狀態。這個槽在原節點的狀態爲migrating,在目標節點的狀態爲importing,表示數據正在從源流向目標。

redis-trib首先會在源和目標節點設置好中間過渡狀態,然後一次性獲取源節點槽位的所有key列表(keysinslot指令),再挨個key進行遷移。每個key的遷移過程是以原節點作爲目標節點的「客戶端」,原節點對當前的key執行dump指令得到序列化內容,然後通過「客戶端」向目標節點發送指令restore攜帶序列化的內容作爲參數,目標節點再進行反序列化就可以將內容恢復到目標節點的內存中,然後返回「客戶端」OK,原節點「客戶端」收到後再把當前節點的key刪除掉就完成了單個key遷移的整個過程。

這裏的遷移過程是同步的,在目標節點執行restore指令到原節點刪除key之間,原節點的主線程會處於阻塞狀態,直到key被成功刪除。

如果遷移過程中突然出現網絡故障,整個槽位遷移只進行了一半。這時兩個節點依舊處於中間過渡狀態。待下次遷移工具重新連上時,會提示用戶繼續進行遷移。

在遷移過程中,如果每個key的內容都很小,migrate指令執行會很快,它就並不會影響客戶端的正常訪問。如果key的內容很大,因爲migrate指令是阻塞指令會同時導致原節點和目標節點卡頓,影響集羣的穩定型。所以在集羣環境下業務邏輯要儘可能避免大key的產生。

在遷移過程中,客戶端訪問的流程會有很大的變化。

首先新舊兩個節點對應的槽位都存在部分 key 數據。客戶端先嚐試訪問舊節點,如果對應的數據還在舊節點裏面,那麼舊節點正常處理。如果對應的數據不在舊節點裏面,那麼有兩種可能,要麼該數據在新節點裏,要麼根本就不存在。舊節點不知道是哪種情況,所以它會向客戶端返回一個-ASK targetNodeAddr的重定向指令。客戶端收到這個重定向指令後,先去目標節點執行一個不帶任何參數的asking指令,然後在目標節點再重新執行原先的操作指令。

爲什麼需要執行一個不帶參數的asking指令呢?

因爲在遷移沒有完成之前,按理說這個槽位還是不歸新節點管理的,如果這個時候向目標節點發送該槽位的指令,節點是不認的,它會向客戶端返回一個-MOVED重定向指令告訴它去源節點去執行。如此就會形成 重定向循環。asking指令的目標就是打開目標節點的選項,告訴它下一條指令不能不理,而要當成自己的槽位來處理。

從以上過程可以看出,遷移是會影響服務效率的,同樣的指令在正常情況下一個 ttl 就能完成,而在遷移中得 3 個 ttl 才能搞定。

問題5:如何確保每個Redis節點高可靠性?

答:Cluster可以爲每個主節點設置若干個從節點,單主節點故障時,集羣會自動將其中某個從節點提升爲主節點。如果某個主節點沒有從節點,那麼當它發生故障時,集羣將完全處於不可用狀態。不過 Redis 也提供了一個參數cluster-require-full-coverage可以允許部分節點故障,其它節點還可以繼續提供對外訪問。

問題6:如何確定某個Redis節點發生網絡通信故障?

答:Cluster選項cluster-node-timeout,表示當某個節點持續 timeout 的時間失聯時,纔可以認定該節點出現故障,需要進行主從切換。還有另外一個選項cluster-slave-validity-factor作爲倍乘係數來放大這個超時時間來寬鬆容錯的緊急程度。如果這個係數爲零,那麼主從切換是不會抗拒網絡抖動的。如果這個係數大於 1,它就成了主從切換的鬆弛係數。

問題7:Cluster如何確定何時對某個節點進行主從切換?

答:Cluster是去中心化,一個節點認爲某個節點失聯了並不代表所有的節點都認爲它失聯了。所以集羣還得經過一次協商的過程,只有當大多數節點都認定了某個節點失聯了,集羣才認爲該節點需要進行主從切換來容錯。

Redis集羣節點採用Gossip協議來廣播自己的狀態以及自己對整個集羣認知的改變。比如一個節點發現某個節點失聯了PFail),它會將這條信息向整個集羣廣播,其它節點也就可以收到這點失聯信息。如果一個節點收到了某個節點失聯的數量(PFail Count)已經達到了集羣的大多數,就可以標記該節點爲確定下線狀態,然後向整個集羣廣播,強迫其它節點也接收該節點已經下線的事實,並立即對該失聯節點進行主從切換。

問題8:Cluster槽位映射關係發生變化之後,如何通知客戶端更新?

答:通過moved、asking指令來完成通知。

moved是用來糾正槽位的。如果我們將指令發送到了錯誤的節點,該節點發現對應的指令槽位不歸自己管理,就會將目標節點的地址隨同moved指令回覆給客戶端通知客戶端去目標節點去訪問。這個時候客戶端就會刷新自己的槽位關係表,然後重試指令,後續所有打在該槽位的指令都會轉到目標節點。

asking指令是用來臨時糾正槽位的。如果當前槽位正處於遷移中,指令會先被髮送到槽位所在的舊節點,如果舊節點存在數據,那就直接返回結果了,如果不存在,那麼它可能真的不存在也可能在遷移目標節點上。所以舊節點會通知客戶端去新節點嘗試一下拿數據,看看新節點有沒有。這時候就會給客戶端返回一個asking error攜帶上目標節點的地址。客戶端收到這個asking error後,就會去目標節點去嘗試。客戶端不會刷新槽位映射關係表,因爲它只是臨時糾正該指令的槽位信息,不影響後續指令。

鑑於moved 和 asking返回結果的不同,客戶端會重試很多次,因此建議客戶端設置一個閾值,在這個閾值範圍內進行for循環直到拿到結果,或者,超過閾值後拋出異常。

問題9:Cluster對某個節點進行主從切換後,如何通知客戶端更新?

答:這裏要分 2 種情況:

情況1:當目標節點掛掉時,客戶端會拋出一個 ConnectionError,緊接着會隨機挑一個節點來重試,這時被重試的節點會通過 moved error告知目標槽位被分配到的新的節點地址。

情況2:運維手動修改了集羣信息,將主節點切換到其它節點,並將舊的主節點移除集羣。這時打在舊節點上的指令會收到一個 ClusterDown的錯誤,告知當前節點所在集羣不可用。這時客戶端就會關閉所有的連接,清空槽位映射關係表,然後向上層拋錯。待下一條指令過來時,就會重新嘗試初始化節點信息。

發佈了167 篇原創文章 · 獲贊 10 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章