Redis分區實現原理

摘要

Redis Partitioning即Redis分區,簡單的說就是將數據分佈到不一樣的redis實例中,所以對於每一個redis實例所存儲的內容僅僅是全部內容的一個子集。分區(Partitioning)不只僅是Redis中的概念,幾乎是全部數據存儲系統都會涉及到的概念,這篇文章將會在理解分區基本概念的基礎之上進一步瞭解Redis對分區的支持。git

 

咱們爲何要分區

咱們爲何要分區?分區的動機是什麼?一般來講,Redis分區的好處大體有以下兩個方面:github

  1. 性能的提高,單機Redis的網絡I/O能力和計算資源是有限的,將請求分散到多臺機器,充分利用多臺機器的計算能力可網絡帶寬,有助於提升Redis整體的服務能力。
  2. 存儲的橫向擴展,即便Redis的服務能力可以知足應用需求,可是隨着存儲數據的增長,單臺機器受限於機器自己的存儲容量,將數據分散到多臺機器上存儲使得Redis服務能夠橫向擴展。

總的來講,分區使得咱們原本受限於單臺計算機硬件資源的問題再也不是問題,存儲不夠?計算資源不夠?帶寬不夠?咱們均可以經過增長機器來解決這些問題。redis

 

Redis分區基礎

實際應用中有不少分區的具體策略,舉個例子,假設咱們已經有了一組四個Redis實例分別爲R0、R一、R二、R3,另外咱們有一批表明用戶的鍵,如:user:1,user:2,……等等,其中「user:」後面的數字表明的是用戶的ID,咱們要作的事情是把這些鍵分散存儲在這四個不一樣的Redis實例上。怎麼作呢?最簡單的一種方式是範圍分區(range partitioning),下面咱們來看看基於範圍分區怎麼作。緩存

範圍分區

所謂範圍分區,就是將一個範圍內的key都映射到同一個Redis實例中,加入數據集仍是上面提到的用戶數據,具體作法以下:服務器

咱們能夠將用戶ID從0到10000的用戶數據映射到R0實例,而將用戶ID從10001到20000的對象映射到R1實例,依次類推。

這種方法雖然簡單,可是在實際應用中是頗有效的,不過仍是有問題:網絡

  • 咱們須要一張表,這張表用來存儲用戶ID範圍到Redis實例的映射關係,好比用戶ID0-10000的是映射到R0實例……。
  • 咱們不只須要對這張表進行維護,並且對於每種對象類型咱們都須要一個這樣的表,好比咱們當前存儲的是用戶信息,若是存儲的是訂單信息,咱們就須要再建一張映射關係表。
  • 若是咱們想要存儲的數據的key並不能按照範圍劃分怎麼辦,好比咱們的key是一組uuid,這個時候就很差用範圍分區了。

所以,在實際應用中,範圍分區並非很好的選擇,不用擔憂,咱們還有更好的方法,接下來認識下哈希分區。分佈式

哈希分區

哈希分區跟範圍分區相比一個明顯的優勢是哈希分區適合任何形式的key,而不像範圍分區同樣須要key的形式爲object_name:<id>,並且分區方法也很簡單,一個公式就能夠表達:memcached

id=hash(key)%N

其中id表明Redis實例的編號,公式描述的是首先根據key和一個hash函數(如crc32函數)計算出一個數值型的值。接着上面的例子,咱們的第一個要處理的key是user:1,hash(user:1)的結果是93024922。函數

而後哈希結果進行取模,取模的目的是計算出一個介於0到3之間的值,所以這個值才能夠被映射到咱們的一臺Redis實例上面。好比93024922%4結果是2,咱們就會知道foobar將要被存儲在R2上面。性能

固然除了上面提到的兩種分區方法,還有不少其餘的方法。好比一種從哈希分區演進而來的consistent hashing分區,相信信息能夠參考個人另外一篇文章《memcached分佈式實現原理》,其已經被redis client和proxies實現了。

 

不一樣的分區實現

分區能夠在redis軟件棧的不一樣部分被實現,咱們來看看下面幾種:

客戶端實現

客戶端實現即key在redis客戶端就決定了要被存儲在那臺Redis實例中,見下圖:

客戶端實現分區示意圖

上面爲客戶端實現Redis分區的示意圖。

代理實現

代理實現即客戶端將請求發往代理服務器,代理服務器實現了Redis協議,所以代理服務器能夠代理客戶端和Redis服務器通訊。代理服務器經過配置的分區schema來將客戶端的請求轉發到正確的Redis實例中,同時將反饋消息返回給客戶端。代理實現Redis分區示意圖以下:

代理實現Redis分區示意圖

Redis和Memcached代理Twemoroxy都實現了代理分區。

查詢路由

查詢路由是Redis Cluster實現的一種Redis分區方式:

查詢路由Redis分區示意圖

查詢路由的過程當中,咱們能夠將查詢請求隨機的發送到任意一個Redis實例,這個Redis實例負責將請求轉發至正確的Redis實例中。Redis集羣實現了一個經過和客戶端協做的hybrid來作查詢路由。

 

Redis分區的缺點

儘管Redis分區到如今爲止,so far so good,可是Redis分區有一些致命的缺點,這致使一些Redis功能在分區的環境下並不能很好地工做,咱們來看看:

  • 多鍵操做是不被支持的,好比咱們將要批量操做的鍵被映射到了不一樣的Redis實例中。
  • 多鍵的Redis事務是不被支持的。
  • 分區的最小粒度是鍵,所以咱們不能將關聯到一個鍵的很大的數據集映射到不一樣的實例。
  • 當應用分區的時候,數據的處理是很是複雜的,好比咱們須要處理多個rdb/aof文件,將分佈在不一樣實例的文件彙集到一塊兒備份。
  • 添加和刪除機器是很複雜的,例如Redis集羣支持幾乎運行時透明的由於增長或減小機器而須要作的rebalancing,然而像客戶端和代理分區這種方式是不支持這種功能的。

既然有問題,那麼就須要解決方案,這個時候Pre-sharding來了,後面咱們會介紹Pre-Sharding。

 

持久存儲用仍是緩存

儘管數據分區對於Redis來講不管是數據持久化存儲仍是緩存,在概念上都是同樣的,然而對於數據持久化存儲仍是有一個很大的限制。當咱們使用Redis來做爲持久化存儲的時候,每個key必須一直被映射到同一個Redis實例。而當Redis被當作緩存使用的時候,對於這個key,若是一個實例不能用了,這個key還能夠被映射到其餘的實例中。

Consistent hashing實現一般使得當一個key被映射到的實例不能用的時候將這個key映射到其餘實例成爲可能。相似,若是增長了一臺機器,一部分的key將會被映射到這臺新的機器上,咱們須要瞭解的兩點以下:

  1. 若是Redis被用來當作緩存,且要求容易增長或刪除機器,使用consistent hashing是很是簡單的。
  2. 若是Redis被用來當作(持久)存儲,一個固定的key到實例的映射是須要的,所以咱們不可以再靈活的添加或刪除機器。不然,咱們須要在增長或刪除機器的時候系統可以rebalace,當前Redis Cluster已經支持。

 

Pre-Sharding

經過上面的介紹,咱們知道Redis分區應用起來是有問題的,除非咱們只是使用Redis當作緩存,不然對於增長機器或刪除機器是很是麻煩的。

然而,一般咱們Redis容量變更在實際應用中是很是常見的,好比今天我須要10臺Redis機器,明天可能就須要50臺機器了。

鑑於Redis是很輕量級的服務(每一個實例僅僅佔用1M),對於上面的問題一種簡單的解決辦法是:

咱們能夠開啓多個Redis實例,儘管是一臺物理機器,咱們在剛開始的時候也能夠開啓多個實例。咱們能夠從中選擇一些實例,好比32或64個實例來做爲咱們的工做集羣。當一臺物理機器存儲不夠的時候,咱們能夠將通常的實例移動到咱們的第二臺物理機上,依次類對,咱們能夠保證集羣中Redis的實例數不變,又能夠達到擴充機器的目的。

怎麼移動Redis實例呢?當須要將Redis實例移動到獨立的機器上的時候,咱們能夠經過下面步驟實現:

  1. 在新的物理機上啓動一個新的Redis實例。
  2. 將新的物理機做爲要移動的那臺的slave機器。
  3. 中止客戶端。
  4. 更新將要被移動的那臺Redis實例的IP地址。
  5. 對於slave機器發送SLAVEOF ON ONE命令。
  6. 使用新的IP啓動Redis客戶端。
  7. 關閉再也不使用的那個Redis實例。

 

總結

這篇文章在理解Redis分區概念的基礎之上又介紹了Redis分區常見的幾種實現方式及原理,最後根據實現中遇到的問題引入了Pre-Sharding解決方案。

 

參考文獻

《Redis官方文檔》

 

注:本文由博主根據經過Redis文檔的學習而原創,若有問題還請多多包涵。

相關文章
相關標籤/搜索