1. 程式人生 > >REDIS 進階(12) redis分片

REDIS 進階(12) redis分片

分片(partitioning)就是將你的資料拆分到多個 Redis 例項的過程,這樣每個例項將只包含所有鍵的子集。

本文第一部分將向你介紹分片的概念,第二部分將向你展示 Redis 分片的可選方案。

分片能做什麼

Redis 的分片承擔著兩個主要目標:
允許使用很多電腦的記憶體總和來支援更大的資料庫。沒有分片,你就被侷限於單機能支援的記憶體容量。

允許伸縮計算能力到多核或多伺服器,伸縮網路頻寬到多伺服器或多網路介面卡。

分片基礎

有很多不同的分片標準(criteria)。假想我們有 4 個 Redis 例項 R0,R1,R2,R3,還有很多表示使用者的鍵,像 user:1,user:2,… 等等,我們能找到不同的方式來選擇一個指定的鍵儲存在哪個例項中。換句話說,有許多不同的辦法來對映一個鍵到一個指定的 Redis 伺服器。
最簡單的執行分片的方式之一是範圍分片(range partitioning),通過對映物件的範圍到指定的 Redis 例項來完成分片。例如,我可以假設使用者從 ID 0 到 ID 10000 進入例項 R0,使用者從 ID 10001 到 ID 20000 進入例項 R1,等等。
這套辦法行得通,並且事實上在實踐中被採用,然而,這有一個缺點,就是需要一個對映範圍到例項的表格。這張表需要管理,不同型別的物件都需要一個表,所以範圍分片在 Redis 中常常並不可取,因為這要比替他分片可選方案低效得多。
一種範圍分片的替代方案是雜湊分片(hash partitioning)。這種模式適用於任何鍵,不需要鍵像 object_name: 這樣的餓形式,就像這樣簡單:
使用一個雜湊函式(例如,crc32 雜湊函式) 將鍵名轉換為一個數字。例如,如果鍵是 foobar,crc32(foobar)將會輸出類似於 93024922 的東西。
對這個資料進行取模運算,以將其轉換為一個 0 到 3 之間的數字,這樣這個數字就可以對映到我的 4 臺 Redis 例項之一。93024922 模 4 等於 2,所以我知道我的鍵 foobar 應當儲存到 R2 例項。注意:取模操作返回除法操作的餘數,在許多程式語言總實現為%操作符。
有許多其他的方式可以分片,從這兩個例子中你就可以知道了。一種雜湊分片的高階形式稱為一致性雜湊(consistent hashing),被一些 Redis 客戶端和代理實現。


分片的不同實現

分片可由軟體棧中的不同部分來承擔。
客戶端分片(Client side partitioning)意味著,客戶端直接選擇正確的節點來寫入和讀取指定鍵。許多 Redis 客戶端實現了客戶端分片。
代理協助分片(Proxy assisted partitioning)意味著,我們的客戶端傳送請求到一個可以理解 Redis 協議的代理上,而不是直接傳送請求到 Redis 例項上。代理會根據配置好的分片模式,來保證轉發我們的請求到正確的 Redis 例項,並返回響應給客戶端。Redis 和 Memcached 的代理 Twemproxy 實現了代理協助的分片。
查詢路由(Query routing)意味著,你可以傳送你的查詢到一個隨機例項,這個例項會保證轉發你的查詢到正確的節點。Redis 叢集在客戶端的幫助下,實現了查詢路由的一種混合形式 (請求不是直接從 Redis 例項轉發到另一個,而是客戶端收到重定向到正確的節點)。

分片的缺點

Redis 的一些特性與分片在一起時玩轉的不是很好:
涉及多個鍵的操作通常不支援。例如,你不能對對映在兩個不同 Redis 例項上的鍵執行交集(事實上有辦法做到,但不是直接這麼幹)。
涉及多個鍵的事務不能使用。
分片的粒度(granularity)是鍵,所以不能使用一個很大的鍵來分片資料集,例如一個很大的有序集合。
當使用了分片,資料處理變得更復雜,例如,你需要處理多個 RDB/AOF 檔案,備份資料時你需要聚合多個例項和主機的持久化檔案。
新增和刪除容量也很複雜。例如,Redis 叢集具有執行時動態新增和刪除節點的能力來支援透明地再均衡資料,但是其他方式,像客戶端分片和代理都不支援這個特性。但是,有一種稱為預分片(Presharding)的技術在這一點上能幫上忙。

資料儲存還是快取

儘管無論是將 Redis 作為資料儲存還是快取,Redis 的分片概念上都是一樣的,但是作為資料儲存時有一個重要的侷限。當 Redis 作為資料儲存時,一個給定的鍵總是對映到相同的 Redis 例項。當 Redis 作為快取時,如果一個節點不可用而使用另一個節點,這並不是一個什麼大問題,按照我們的願望來改變鍵和例項的對映來改進系統的可用性(就是系統回覆我們查詢的能力)。
一致性雜湊實現常常能夠在指定鍵的首選節點不可用時切換到其他節點。類似的,如果你新增一個新節點,部分資料就會開始被儲存到這個新節點上。


這裡的主要概念如下:
如果 Redis 用作快取,使用一致性雜湊來來實現伸縮擴充套件(scaling up and down)是很容易的。

如果 Redis 用作儲存,使用固定的鍵到節點的對映,所以節點的數量必須固定不能改變。否則,當增刪節點時,就需要一個支援再平衡節點間鍵的系統,當前只有 Redis 叢集可以做到這一點,但是 Redis 叢集現在還處在 beta 階段,尚未考慮再生產環境中使用。

預分片

我們已經知道分片存在的一個問題,除非我們使用 Redis 作為快取,增加和刪除節點是一件很棘手的事情,使用固定的鍵和例項對映要簡單得多。

然而,資料儲存的需求可能一直在變化。今天我可以接受 10 個 Redis 節點(例項),但是明天我可能就需要 50 個節點。


因為 Redis 只有相當少的記憶體佔用(footprint)而且輕量級(一個空閒的例項只是用 1MB 記憶體),一個簡單的解決辦法是一開始就開啟很多的例項。即使你一開始只有一臺伺服器,你也可以在第一天就決定生活在分散式的世界裡,使用分片來執行多個 Redis 例項在一臺伺服器上。
你一開始就可以選擇很多數量的例項。例如,32 或者 64 個例項能滿足大多數的使用者,並且為未來的增長提供足夠的空間。
這樣,當你的資料儲存需要增長,你需要更多的 Redis 伺服器,你要做的就是簡單地將例項從一臺伺服器移動到另外一臺。當你新添加了第一臺伺服器,你就需要把一半的 Redis 例項從第一臺伺服器搬到第二臺,如此等等。

使用 Redis 複製,你就可以在很小或者根本不需要停機時間內完成移動資料:
在你的新伺服器上啟動一個空例項。
移動資料,配置新例項為源例項的從服務。
停止你的客戶端。
更新被移動例項的伺服器 IP 地址配置。
向新伺服器上的從節點發送 SLAVEOF NO ONE 命令。
以新的更新配置啟動你的客戶端。

最後關閉掉舊伺服器上不再使用的例項。

Redis 分片的實現

Redis 叢集是自動分片和高可用的首選方式。當前還不能完全用於生產環境,但是已經進入了 beta 階段。
一旦 Redis 叢集可用,以及支援 Redis 叢集的客戶端可用,Redis 叢集將會成為 Redis 分片的事實標準。

Redis 叢集是查詢路由和客戶端分片的混合模式。
Twemproxy 是 Twitter 開發的一個支援 Memcached ASCII 和 Redis 協議的代理。它是單執行緒的,由 C 語言編寫,執行非常的快。他是基於 Apache 2.0 許可的開源專案。


Twemproxy 支援自動在多個 Redis 例項間分片,如果節點不可用時,還有可選的節點排除支援(這會改變鍵和例項的對映,所以你應該只在將 Redis 作為快取是才使用這個特性)。


這並不是單點故障(single point of failure),因為你可以啟動多個代理,並且讓你的客戶端連線到第一個接受連線的代理。

Twemproxy 之外的可選方案,是使用實現了客戶端分片的客戶端,通過一致性雜湊或者別的類似演算法。有多個支援一致性雜湊的 Redis 客戶端,例如 Redis-rb 和 Predis。