Redis基礎篇(八)數據分片


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

第一個方案,也是最容易想到的,需要保存5000萬個鍵值對,每個鍵值對約為512B,一共需要25GB空間,選擇一台32GB內存的用品來部署Redis,還剩余7GB空間,可以采用RDB對數據做持續久。

但是Redis服務使用不久后出現Redis的響應有時會非常慢。原因是采用了RDB持久化,在前面介紹RDB原理時,我們知道fork子進程的瞬間會阻塞主線程,而且內存越大,阻塞越長。

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

這里就引出一個問題:該如何保存更多的數據?

如何保存更多數據

通常有兩種方案,分別是縱向擴展和橫向擴展。

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

橫向擴展,指通過增加機器來組成更大的集群,這也是分布式方案常用的方式。好處是擴展性好,但缺點是管理復雜。

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

在使用單個實例時,數據保存在哪里,客戶端訪問哪里,都是非常明確的。但是切片集群不可避免要解決多個實例分布式管理的問題,需要解決兩大問題:

  • 數據切片后,在多個實例之間如何分布?
  • 客戶端怎么確定想要訪問的數據在哪個實例上?

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

在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等。

參考資料


免責聲明!

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



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