redis基礎---切片集群


在實際應用 Redis 時,隨着用戶或業務規模的擴展,保存大量數據的情況通常是無法避免的。而切片集群,就是一個非常好的解決方案。

在使用 RDB 進行持久化時,Redis 會 fork 子進程來完成,fork 操作的用時和 Redis 的數據量是正相關的,而 fork 在執行時會阻塞主線程。數據量越大,fork 操作造成的主線程阻塞的時間越長。所以,在使用 RDB 對 25GB 的數據進行持久化時,數據量較大,后台運行的子進程在 fork 創建時阻塞了主線程,於是就導致 Redis 響應變慢了。

切片集群,也叫分片集群,就是指啟動多個 Redis 實例組成一個集群,然后按照一定的規則,把收到的數據划分成多份,每一份用一個實例來保存。

一主多從是數據復制,用來解決數據並發量大所帶來的壓力,而切片集群是數據切片,是用來解決單個實例存儲大數據量的局限性,一主多從是用RDB來實現的,而切片集群是用哈希槽來實現的。

 

Redis 應對數據量增多的兩種方案:縱向擴展(scale up)和橫向擴展(scale out)。

縱向擴展:升級單個 Redis 實例的資源配置,包括增加內存容量、增加磁盤容量、使用更高配置的 CPU。就像下圖中,原來的實例內存是 8GB,硬盤是 50GB,縱向擴展后,內存增加到 24GB,磁盤增加到 150GB。

橫向擴展:橫向增加當前 Redis 實例的個數,就像下圖中,原來使用 1 個 8GB 內存、50GB 磁盤的實例,現在使用三個相同配置的實例。

 

 

 

 縱向擴展會受到硬件和成本的限制。在面向百萬、千萬級別的用戶規模時,橫向擴展的 Redis 切片集群會是一個非常好的選擇。

數據切片和實例的對應分布關系

在切片集群中,數據需要分布在不同實例上。從 3.0 開始,官方提供了一個名為 Redis Cluster 的方案,用於實現切片集群。Redis Cluster 方案中就規定了數據和實例的對應規則。

Redis Cluster 方案采用哈希槽(Hash Slot,接下來我會直接稱之為 Slot),來處理數據和實例之間的映射關系。在 Redis Cluster 方案中,一個切片集群共有 16384 個哈希槽,這些哈希槽類似於數據分區,每個鍵值對都會根據它的 key,被映射到一個哈希槽中。

具體的映射過程分為兩大步:

  首先根據鍵值對的 key,按照CRC16 算法計算一個 16 bit 的值;然后,再用這個 16bit 值對 16384 取模,得到 0~16383 范圍內的模數,每個模數代表一個相應編號的哈希槽。

16384個哈希槽怎么分布到多個實例中?

1.使用 cluster create 創建集群可以平均把哈希槽分到各個實例,每個實例的哈希槽個數是 16384/N 個

2.可以使用cluster addslots 根據不同實例的性能自定義不同實例哈希槽的個數。在手動分配哈希槽時,需要把 16384 個槽都分配完,否則 Redis 集群無法正常工作。

 

客戶端如何定位數據?

  客戶端和集群實例建立連接后,實例就會把哈希槽的分配信息發給客戶端。

 

   Redis 實例會把自己的哈希槽信息發給和它相連接的其它實例,來完成哈希槽分配信息的擴散。當實例之間相互連接后,每個實例就有所有哈希槽的映射關系了。

客戶端收到哈希槽信息后,會把哈希槽信息緩存在本地。當客戶端請求鍵值對時,會先計算鍵所對應的哈希槽,然后就可以給相應的實例發送請求了。

但是,在集群中,實例和哈希槽的對應關系並不是一成不變的,最常見的變化有兩個:

  在集群中,實例有新增或刪除,Redis 需要重新分配哈希槽;

  為了負載均衡,Redis 需要把哈希槽在所有實例上重新分布一遍。

此時,實例之間還可以通過相互傳遞消息,獲得最新的哈希槽分配信息,但是,客戶端是無法主動感知這些變化的。

Redis Cluster 方案提供了一種重定向機制,所謂的“重定向”,就是當客戶端把一個鍵值對的操作請求發給一個實例時,如果這個實例上並沒有這個鍵值對映射的哈希槽,那么,這個實例就會給客戶端返回下面的 MOVED 命令響應結果,這個結果中就包含了新實例的訪問地址。


GET hello:key
(error) MOVED 13320 172.16.19.5:6379

其中,MOVED 命令表示,客戶端請求的鍵值對所在的哈希槽 13320,實際是在 172.16.19.5 這個實例上.

 

 

 

 

在實際應用時,如果 Slot 2 中的數據比較多,就可能會出現一種情況:客戶端向實例 2 發送請求,但此時,Slot 2 中的數據只有一部分遷移到了實例 3,還有部分數據沒有遷移。在這種遷移部分完成的情況下,客戶端就會收到一條 ASK 報錯信息,如下所示:


GET hello:key
(error) ASK 13320 172.16.19.5:6379

 

ASK 命令表示兩層含義:第一,表明 Slot 數據還在遷移中;第二,ASK 命令把客戶端所請求數據的最新實例地址返回給客戶端,此時,客戶端需要給實例 3 發送 ASKING 命令,然后再發送操作命令。

ASK 命令並不會更新客戶端緩存的哈希槽分配信息。

 

評論精選

另外,我想補充一下Redis集群相關的知識,以及我的理解:

Redis使用集群方案就是為了解決單個節點數據量大、寫入量大產生的性能瓶頸的問題。多個節點組成一個集群,可以提高集群的性能和可靠性,但隨之而來的就是集群的管理問題,最核心問題有2個:請求路由、數據遷移(擴容/縮容/數據平衡)。

1、請求路由:一般都是采用哈希槽的映射關系表找到指定節點,然后在這個節點上操作的方案。

Redis Cluster在每個節點記錄完整的映射關系(便於糾正客戶端的錯誤路由請求),同時也發給客戶端讓客戶端緩存一份,便於客戶端直接找到指定節點,客戶端與服務端配合完成數據的路由,這需要業務在使用Redis Cluster時,必須升級為集群版的SDK才支持客戶端和服務端的協議交互。

其他Redis集群化方案例如Twemproxy、Codis都是中心化模式(增加Proxy層),客戶端通過Proxy對整個集群進行操作,Proxy后面可以掛N多個Redis實例,Proxy層維護了路由的轉發邏輯。操作Proxy就像是操作一個普通Redis一樣,客戶端也不需要更換SDK,而Redis Cluster是把這些路由邏輯做在了SDK中。當然,增加一層Proxy也會帶來一定的性能損耗。

2、數據遷移:當集群節點不足以支撐業務需求時,就需要擴容節點,擴容就意味着節點之間的數據需要做遷移,而遷移過程中是否會影響到業務,這也是判定一個集群方案是否成熟的標准。

Twemproxy不支持在線擴容,它只解決了請求路由的問題,擴容時需要停機做數據重新分配。而Redis Cluster和Codis都做到了在線擴容(不影響業務或對業務的影響非常小),重點就是在數據遷移過程中,客戶端對於正在遷移的key進行操作時,集群如何處理?還要保證響應正確的結果?

Redis Cluster和Codis都需要服務端和客戶端/Proxy層互相配合,遷移過程中,服務端針對正在遷移的key,需要讓客戶端或Proxy去新節點訪問(重定向),這個過程就是為了保證業務在訪問這些key時依舊不受影響,而且可以得到正確的結果。由於重定向的存在,所以這個期間的訪問延遲會變大。等遷移完成之后,Redis Cluster每個節點會更新路由映射表,同時也會讓客戶端感知到,更新客戶端緩存。Codis會在Proxy層更新路由表,客戶端在整個過程中無感知。

除了訪問正確的節點之外,數據遷移過程中還需要解決異常情況(遷移超時、遷移失敗)、性能問題(如何讓數據遷移更快、bigkey如何處理),這個過程中的細節也很多。

Redis Cluster的數據遷移是同步的,遷移一個key會同時阻塞源節點和目標節點,遷移過程中會有性能問題。而Codis提供了異步遷移數據的方案,遷移速度更快,對性能影響最小,當然,實現方案也比較復雜。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM