Redis 分割槽實現原理

Float_Lu發表於2016-06-11

摘要

Redis Partitioning即Redis分割槽,簡單的說就是將資料分佈到不同的redis例項中,因此對於每個redis例項所儲存的內容僅僅是所有內容的一個子集。分割槽(Partitioning)不僅僅是Redis中的概念,幾乎是所有資料儲存系統都會涉及到的概念,這篇文章將會在理解分割槽基本概念的基礎之上進一步瞭解Redis對分割槽的支援。

我們為什麼要分割槽

我們為什麼要分割槽?分割槽的動機是什麼?通常來說,Redis分割槽的好處大致有如下兩個方面:

  1. 效能的提升,單機Redis的網路I/O能力和計算資源是有限的,將請求分散到多臺機器,充分利用多臺機器的計算能力可網路頻寬,有助於提高Redis總體的服務能力。
  2. 儲存的橫向擴充套件,即使Redis的服務能力能夠滿足應用需求,但是隨著儲存資料的增加,單臺機器受限於機器本身的儲存容量,將資料分散到多臺機器上儲存使得Redis服務可以橫向擴充套件。

總的來說,分割槽使得我們本來受限於單臺計算機硬體資源的問題不再是問題,儲存不夠?計算資源不夠?頻寬不夠?我們都可以通過增加機器來解決這些問題。

Redis分割槽基礎

實際應用中有很多分割槽的具體策略,舉個例子,假設我們已經有了一組四個Redis例項分別為R0、R1、R2、R3,另外我們有一批代表使用者的鍵,如:user:1,user:2,……等等,其中“user:”後面的數字代表的是使用者的ID,我們要做的事情是把這些鍵分散儲存在這四個不同的Redis例項上。怎麼做呢?最簡單的一種方式是範圍分割槽(range partitioning),下面我們來看看基於範圍分割槽怎麼做。

範圍分割槽

所謂範圍分割槽,就是將一個範圍內的key都對映到同一個Redis例項中,加入資料集還是上面提到的使用者資料,具體做法如下:

我們可以將使用者ID從010000的使用者資料對映到R0例項,而將使用者ID從1000120000的物件對映到R1例項,依次類推。

這種方法雖然簡單,但是在實際應用中是很有效的,不過還是有問題:

  • 我們需要一張表,這張表用來儲存使用者ID範圍到Redis例項的對映關係,比如使用者ID0-10000的是對映到R0例項……。
  • 我們不僅需要對這張表進行維護,而且對於每種物件型別我們都需要一個這樣的表,比如我們當前儲存的是使用者資訊,如果儲存的是訂單資訊,我們就需要再建一張對映關係表。
  • 如果我們想要儲存的資料的key並不能按照範圍劃分怎麼辦,比如我們的key是一組uuid,這個時候就不好用範圍分割槽了。

因此,在實際應用中,範圍分割槽並不是很好的選擇,不用擔心,我們還有更好的方法,接下來認識下雜湊分割槽。

雜湊分割槽

雜湊分割槽跟範圍分割槽相比一個明顯的優點是雜湊分割槽適合任何形式的key,而不像範圍分割槽一樣需要key的形式為object_name:<id>,而且分割槽方法也很簡單,一個公式就可以表達:

其中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例項中,見下圖:

21

客戶端實現分割槽示意圖

上面為客戶端實現Redis分割槽的示意圖。

代理實現

代理實現即客戶端將請求發往代理伺服器,代理伺服器實現了Redis協議,因此代理伺服器可以代理客戶端和Redis伺服器通訊。代理伺服器通過配置的分割槽schema來將客戶端的請求轉發到正確的Redis例項中,同時將反饋訊息返回給客戶端。代理實現Redis分割槽示意圖如下:

22

代理實現Redis分割槽示意圖

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

查詢路由

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

23

查詢路由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 分割槽實現原理 Redis 分割槽實現原理

相關文章