Redis基礎篇(八)數據分片

如今有一個場景:要用Redis保存5000萬個鍵值對,每一個鍵值對大約是512B,要怎麼部署Redis服務呢?html

第一個方案,也是最容易想到的,須要保存5000萬個鍵值對,每一個鍵值對約爲512B,一共須要25GB空間,選擇一臺32GB內存的用品來部署Redis,還剩餘7GB空間,能夠採用RDB對數據作持續久。算法

可是Redis服務使用不久後出現Redis的響應有時會很是慢。緣由是採用了RDB持久化,在前面介紹RDB原理時,咱們知道fork子進程的瞬間會阻塞主線程,並且內存越大,阻塞越長。緩存

第一個方案不太適合,那麼有更好的方案嗎?Redis提供切片集羣機制,多個Redis實例組成一個集羣,按照必定的規則,把收到數據劃分紅多份,每一份用一個實例來保存。這樣一來,在生成RDB時,數據量就小了,fork就不會阻塞主線程太長時間。網絡

這裏就引出一個問題:該如何保存更多的數據?負載均衡

如何保存更多數據

一般有兩種方案,分別是縱向擴展和橫向擴展。分佈式

縱向擴展,指經過增長硬件配置來擴展,採用更大的內存,更多的CPU。好處是實施簡單,但缺點是受到硬件和成本的限制,不可能無限擴展。spa

橫向擴展,指經過增長機器來組成更大的集羣,這也是分佈式方案經常使用的方式。好處是擴展性好,但缺點是管理複雜。線程

在面向百萬、千萬級別的用戶規模時,橫向擴展的Redis切片集羣會是一個很是好的選擇。代理

在使用單個實例時,數據保存在哪裏,客戶端訪問哪裏,都是很是明確的。可是切片集羣不可避免要解決多個實例分佈式管理的問題,須要解決兩大問題:code

  • 數據切片後,在多個實例之間如何分佈?
  • 客戶端怎麼肯定想要訪問的數據在哪一個實例上?

數據切片和實例的對應分佈關係

在Redis 3.0以前,官方沒有切片集羣的方案,從3.0開始,官方提供了一個名爲Redis Cluster的方案,用於實現切片集羣。

Redis Cluster方案採用哈希槽來處理數據和實例之間的映射關係。這裏有兩個映射關係:鍵值對與哈希槽的映射關係和哈希槽與實例的映射關係。下面咱們來介紹一下這兩個映射關係的映射過程。

鍵值對與哈希槽的映射過程

根據鍵值對的key,按照CRC16算法計算一個16bit的值。

再用這個16bit值對16384取模,獲得0~16383範圍內的模數,每一個模數表明一個相應編號的哈希槽。

說明:Redis切片集羣最多提供16384個哈希槽。

哈希槽與實例的映射過程

哈希槽與實例的映射關係有兩個方案設置,分爲自動和手動。

自動映射:使用cluster create命令建立集羣,Redis會自動把這些槽平均分佈在集羣實例上。

手動映射:使用cluster meet命令搬運創建實例間的鏈接,造成集羣,再使用cluster addslots命令,指定每一個實例上的哈希槽個數。

說明:在手動分配哈希槽時,須要把16384個槽都分配完,不然Redis集羣沒法正常工做。

客戶端如何定位數據

客戶端和集羣實例創建鏈接後,實例就會把哈希槽的分配信息發給客戶端。

集羣剛建立時,實例如何互相知道哈希槽信息?Redis實例會擴展哈希槽信息,每一個Redis實例都擁有完整的哈希槽信息。

另外,客戶端收到哈希槽信息後,會緩存在本地,以便在客戶端後續請求直接訪問實例。

但在集羣中,實例和哈希槽的對應關係不是一成不變的。最多見的變化:

  • 在集羣中,實例有新增或刪除,Redis須要從新分配哈希槽;
  • 爲了負載均衡,Redis須要把哈希槽在全部實例上從新分佈一遍。

Redis Cluster提供一種重定向機制,相似於HTTP協議的重定向。

客戶端把一個鍵值對操做請求發給一個實例,若是這個實例沒有這個鍵值對映射的哈希槽,這個實例就會給客戶端返回MOVED命令的響應結果,包含新實例的訪問地址。

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

 

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

若是哈希槽沒有完成遷移,客戶端請求的數據並不在哈希槽時,客戶端就會收到一條ASK報錯信息,以下所示:

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

 

這個結果中的ASK命令就表示,客戶端請求的鍵值對所在的哈希槽13320,在172.16.19.5這個實例上,可是這個哈希槽正在遷移。

和MOVED命令不一樣,ASK命令並不會更新客戶端緩存的哈希槽分配信息

Redis Cluster爲何不採用把key直接映射到實例的方式

整個集羣存儲key的數量是沒法預估的,key的數量很是多時,直接記錄每一個key對應的實例映射關係,這個映射表會很是龐大,這個映射表不管是存儲在服務端仍是客戶端都佔用了很是大的內存空間。

Redis Cluster採用無中心化的模式(無proxy,客戶端與服務端直連),客戶端在某個節點訪問一個key,若是這個key不在這個節點上,這個節點須要有糾正客戶端路由到正確節點的能力(MOVED響應),這就須要節點之間互相交換路由表,每一個節點擁有整個集羣完整的路由關係。若是存儲的都是key與實例的對應關係,節點之間交換信息也會變得很是龐大,消耗過多的網絡資源,並且就算交換完成,至關於每一個節點都須要額外存儲其餘節點的路由表,內存佔用過大形成資源浪費。

當集羣在擴容、縮容、數據均衡時,節點之間會發生數據遷移,遷移時須要修改每一個key的映射關係,維護成本高。

而在中間增長一層哈希槽,能夠把數據和節點解耦,key經過Hash計算,只須要關心映射到了哪一個哈希槽,而後再經過哈希槽和節點的映射表找到節點,至關於消耗了不多的CPU資源,不但讓數據分佈更均勻,還可讓這個映射表變得很小,利於客戶端和服務端保存,節點之間交換信息時也變得輕量。

當集羣在擴容、縮容、數據均衡時,節點之間的操做例如數據遷移,都以哈希槽爲基本單位進行操做,簡化了節點擴容、縮容的難度,便於集羣的維護和管理。

小結

  • 數據擴容有兩種方式:縱向擴展和橫向擴展。Redis切片集羣提供了橫向擴展的模式。
  • 集羣的實例增減或者數據從新分佈,會致使哈希槽和實例的映射關係發生變化。當客戶端發送請求時,會收到命令執行報錯信息。
  • 在Redis3.0以前,Redis官方並無提供切片集羣方案。業界提供了一些成熟的方案,例如基於客戶端分區的ShardedJedis,基於代理的Codis、Twemproxy等。

參考資料

相關文章
相關標籤/搜索