初識 Redis 數據分區

分區:如何在多個 Redis 實例之間拆分數據。

1. 前言

1.1 什麼是分區?

分區是將數據拆分給多個 Redis 實例的過程,因此每個實例將只包含鍵的一個子集。 本文檔的第一部分將向您介紹分區的概念,第二部分將向您展示 Redis 分區的替代方法。

1.2 爲什麼要用分區?

Redis 服務中使用分區主要有兩個目的:

  • 有了分區就可以使用多臺計算機的內存總和,允許 Redis 處理更大的數據集,不進行分區,數據集的大小就會被限制在一臺計算機的內存。
  • 它允許將計算能力擴展到多核和多臺計算機,並將網絡帶寬擴展到多臺計算機和網絡適配器。

2. 分區基礎知識

有多種分區方式。假設我們有4個 Redis 實例,分別是R0, R1, R2, R3,以及許多代表用戶的鍵,如:user:1, uesr:2, …等,我們可以使用多種方法來決定哪個實例存儲哪個鍵。

最簡單的方法是範圍分區range partitioning),將一定範圍的對象映射到指定的 Redis 實例。例如,用戶 ID 從0 ~ 10000分給R0 ,10001 ~ 20000 分給R1,依次類推。

上面這個方法有一個缺點:需要一個表記錄如何將範圍對象映射給實例。並且每種對象都需要這樣一個表,這樣的話,分區效率很低,所以通常都不用range partitioning 方法來分區。

哈希分區hash partitioning) 可以用來替換 range partitioning。哈希分區適用於任何鍵,並不要求鍵的格式是 object_name:id

  • 獲取鍵名,使用 hash 函數將其轉換爲數字。例如,使用crc32("foobar"),將之轉傳承93024922。
  • hash 之後的數字取模,將其轉換成0 ~ 3之間的數字,這對應着 R0 ~ R3 。例如,93024922模4等於2,所以這個鍵應該存在 R2 實例中。

還有許多其他方法可以執行分區,但是通過這兩個示例,您應該瞭解一下。 哈希分區的一種高級形式稱爲 一致性哈希 ,由一些Redis 客戶端和代理實現。

2.1 分區的不同實現

  • 客戶端分區client side partitioning)意味着客戶端直接選擇在哪個節點上寫入或讀取指定的鍵。 許多 Redis客戶端 實現客戶端分區。
  • 代理輔助分區proxy assisted partitioning)意味着我們的客戶將請求發送到能夠使用 Redis 協議的代理,而不是直接將請求發送到正確的 Redis 實例。 代理將確保根據配置的分區架構configured partitioning schema)將請求轉發到正確的 Redis 實例,並將答覆發送回客戶端。 RedisMemcached 代理Twemproxy 實現了代理輔助分區。
  • 查詢路由query routing)意味着你可以將查詢發送到隨機實例,該實例將確保將查詢轉發到正確的節點。 Redis Cluster 在客戶端的幫助下實現了混合形式的查詢路由(請求不會從Redis實例直接轉發到另一個實例,而是會將客戶端重定向到正確的節點)。

2.2 分區的缺點

Redis 的某些功能在分區中不能很好地發揮作用:

  • 通常不支持涉及多個鍵的操作。 例如,將1個鍵映射到多個Redis 實例,不能再這樣的鍵之間求交集(實際上,有很多方法可以執行此操作,但不能直接執行)。
  • 不能使用涉及多個鍵的 Redis 事務。
  • 分區粒度是關鍵,因此無法使用單個大鍵(如非常大的 Sorted set )對數據集進行分片。
  • 使用分區時,數據處理會更加複雜。例如,你必須處理多個RDB / AOF 文件,並且要備份數據,則需要從多個實例和主機聚合持久性文件。
  • 添加和刪除容量可能很複雜。 例如,Redis Cluster 支持大多數透明的數據重新平衡,並能夠在運行時添加和刪除節點,但是其他系統(例如客戶端分區和代理)不支持此功能。 但是,在這方面,一種稱爲“預分片”的技術會有所幫助。

2.3 數據存儲還是緩存?

從概念上來看,無論將 Redis 用作數據存儲還是用作緩存,Redis 中的分區都是相同的,但是在將其用作數據存儲時存在很大的限制。

Redis 用作數據存儲時,指定的鍵必須始終映射到同一 Redis 實例。 當 Redis 用作緩存時,如果給定的節點不可用,則使用不同的節點也不是什麼大問題,因爲我們希望提高系統的可用性。

如果指定的鍵的首選節點不可用,則通常可以通過一致性哈希切換到其他節點。 同樣,如果您添加新節點,則部分新鍵將開始存儲在新節點上。

  • 如果將 Redis 用作緩存,則可以使用一致哈希來進行向上和向下縮放。
  • 如果將 Redis 用作存儲,則使用固定的鍵到節點的映射,因此節點數必須固定並且不能變化。否則,需要一個能夠在添加或刪除節點時在節點之間重新平衡鍵的系統,並且目前只有Redis Cluster 能夠做到這一點。

2.4 預分片(presharding)

從上面的瞭解可知,分區的一個問題是:除非將 Redis 作爲緩存,否則添加和刪除節點十分棘手。(當然,使用固定的key-instances映射也沒這個問題。)

但是,數據存儲常常需要變化,所以固定的key-instances映射不太符合實際。

由於 Redis的佔用空間非常小且重量輕,所以從一開始就可以在多個 Redis 實例使用分區。

爲了儘量少的添加和刪除節點,從一開始就使用大量的 Redis 實例。例如,對於大多數用戶而言,32或64個實例可以解決問題,並將爲增長提供足夠的空間。這樣,隨着數據存儲需求的增加以及您需要更多的 Redis 服務器,您要做的就是將實例從一臺服務器移動到另一臺服務器。 一旦額外添加了第一臺服務器,您將需要將一半 Redis 實例從第一臺服務器移動到第二臺服務器,依此類推。

使用 Redis 副本,您將可以爲用戶減少停機時間或減少停機時間。

3. Redis 分區的實現

3.1 Redis 集羣

Redis Cluster 是獲得自動分片和高可用性的首選方法。Redis Clusterquery routingclient side partitioning 的混合體。

3.2 Twemproxy

Twemproxy支持在多個 Redis 實例之間進行自動分區,並在節點不可用的情況下彈出可選節點(這將更改key-instances映射,因此僅在將 Redis 用作緩存時才應使用此功能)。

基本上,Twemproxy 是客戶端和 Redis 實例之間的中間層,它將以最小的額外複雜性可靠地爲我們處理分區。

3.3 支持一致哈希的客戶端

Twemproxy 的替代方法是使用通過一致性哈希或其他類似算法實現客戶端分區的客戶端。 有多個 Redis 客戶端支持一致性哈希,特別是 Redis-rbPredis

那麼什麼是一致性哈希呢?

還是拿前面的例子來說。4個 Redis 實例,使用簡單哈希的過程如下:

  1. 使用 hash 函數將鍵轉換成數字。
  2. 將此數字對4取模,結果爲0、1、2、3分別對應 Redis 的4個實例R0、 R1、 R2、 R3。

但是,因爲4個 Redis 實例不夠用了,又加了1個 Redis 實例,那麼前面的 key-instances的映射 關係就會發生改變了,如果要解決這個問題就需要重新對所用的鍵的哈希值對5取模,來決定映射到哪一個 Redis 實例上,而且在新映射的機器中沒有之前存的鍵了。

當機器數量發生變動的時候,幾乎所有的數據都會移動,就 Redis 而言可能造成緩存雪崩(在這一時刻,所有緩存都沒了,因爲新映射的機器中沒有存對應的鍵)。此時的問題是,當增加或者刪除節點時,對於大多數記錄,保證原來分配到的某個節點,現在仍然應該分配到那個節點,將數據遷移量的降到最低,這就是一致性哈希要做的事情。在這裏我們不指定是數據庫還是什麼,反正都是分佈式存儲節點。

一致性哈希就是解決的上述問題。從 2 個方向去考慮:

  1. 節點宕機時,數據記錄會被定位到下一個節點上;
  2. 新增節點時,相關區間內的數據記錄就需要重新哈希。

一致性 Hash算法也是使用取模的思想,只是,剛纔描述的取模法是對節點數量進行取模,而一致性Hash算法是對 232 取模,什麼意思呢?簡單來說,一致性Hash算法將整個哈希值空間組織成一個虛擬的圓環,如假設某哈希函數H的值空間爲0 ~ 232-1(即哈希值是一個32位無符號整形),整個哈希環如下,從 0 ~ 232-1 代表的分別是一個個的節點,這個環也叫哈希環

  1. 根據節點ip或者其他進行哈希,並對 232 取模,找到這些節點在哈希環中的位置;
  2. 根據Redis中的鍵進行哈希,並對 232 取模,找到這個鍵在哈希環中的位置;
  3. 從這個鍵的位置順時針找到的第一個節點就是應該映射的節點了。

當服務器節點過少時,使用一致性哈希可能會出現 數據傾斜問題(也就是,被緩存的對象大部分都在某一臺服務器上)。採用 虛擬節點機制,即對每一個服務節點計算多個哈希,每個計算結果位置都放置一個此服務節點,稱爲虛擬節點。具體做法可以在服務器IP主機名的後面增加編號來實現。

4. 參考文獻

[1] Redis 官方文檔 partitioning
[2] 一致性哈希
[3] 面試必備:什麼是一致性Hash算法?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章