鍍金池/ 教程/ 大數(shù)據(jù)/ 分片
使用 Redis 實現(xiàn) Twitter(上)
集群(下)
使用 Redis 實現(xiàn) Twitter(下)
使用 Redis 作為 LRU 緩存
高可用(上)
高可用客戶端指引
集群(中)
高可用(下)
持久化
Redis 介紹
集中插入
集群(上)
從入門到精通(上)
從入門到精通(下)
從入門到精通(中)
分片
數(shù)據(jù)類型初探
復(fù)制

分片

分片(partitioning)就是將你的數(shù)據(jù)拆分到多個 Redis 實例的過程,這樣每個實例將只包含所有鍵的子集。本文第一部分將向你介紹分片的概念,第二部分將向你展示 Redis 分片的可選方案。

分片為何有用(Why useful)

Redis 的分片承擔(dān)著兩個主要目標(biāo):

  • 允許使用很多電腦的內(nèi)存總和來支持更大的數(shù)據(jù)庫。沒有分片,你就被局限于單機能支持的內(nèi)存容量。
  • 允許伸縮計算能力到多核或多服務(wù)器,伸縮網(wǎng)絡(luò)帶寬到多服務(wù)器或多網(wǎng)絡(luò)適配器。

分片基礎(chǔ)(Basics)

有很多不同的分片標(biāo)準(zhǔn)(criteria)。假想我們有 4 個 Redis 實例 R0,R1,R2,R3,還有很多表示用戶的鍵,像 user:1,user:2,… 等等,我們能找到不同的方式來選擇一個指定的鍵存儲在哪個實例中。換句話說,有許多不同的辦法來映射一個鍵到一個指定的 Redis 服務(wù)器。

最簡單的執(zhí)行分片的方式之一是范圍分片(range partitioning),通過映射對象的范圍到指定的 Redis 實例來完成分片。例如,我可以假設(shè)用戶從 ID 0 到 ID 10000 進入實例 R0,用戶從 ID 10001 到 ID 20000 進入實例 R1,等等。

這套辦法行得通,并且事實上在實踐中被采用,然而,這有一個缺點,就是需要一個映射范圍到實例的表格。這張表需要管理,不同類型的對象都需要一個表,所以范圍分片在 Redis 中常常并不可取,因為這要比替他分片可選方案低效得多。

一種范圍分片的替代方案是哈希分片(hash partitioning)。這種模式適用于任何鍵,不需要鍵像 object_name: 這樣的餓形式,就像這樣簡單:

  • 使用一個哈希函數(shù)(例如,crc32 哈希函數(shù)) 將鍵名轉(zhuǎn)換為一個數(shù)字。例如,如果鍵是 foobar,crc32(foobar)將會輸出類似于 93024922 的東西。
  • 對這個數(shù)據(jù)進行取模運算,以將其轉(zhuǎn)換為一個 0 到 3 之間的數(shù)字,這樣這個數(shù)字就可以映射到我的 4 臺 Redis 實例之一。93024922 模 4 等于 2,所以我知道我的鍵 foobar 應(yīng)當(dāng)存儲到 R2 實例。注意:取模操作返回除法操作的余數(shù),在許多編程語言總實現(xiàn)為%操作符。

有許多其他的方式可以分片,從這兩個例子中你就可以知道了。一種哈希分片的高級形式稱為一致性哈希(consistent hashing),被一些 Redis 客戶端和代理實現(xiàn)。

分片的不同實現(xiàn)(Different implementations)

分片可由軟件棧中的不同部分來承擔(dān)。

  • 客戶端分片(Client side partitioning)意味著,客戶端直接選擇正確的節(jié)點來寫入和讀取指定鍵。許多 Redis 客戶端實現(xiàn)了客戶端分片。
  • 代理協(xié)助分片(Proxy assisted partitioning)意味著,我們的客戶端發(fā)送請求到一個可以理解 Redis 協(xié)議的代理上,而不是直接發(fā)送請求到 Redis 實例上。代理會根據(jù)配置好的分片模式,來保證轉(zhuǎn)發(fā)我們的請求到正確的 Redis 實例,并返回響應(yīng)給客戶端。Redis 和 Memcached 的代理 Twemproxy 實現(xiàn)了代理協(xié)助的分片。
  • 查詢路由(Query routing)意味著,你可以發(fā)送你的查詢到一個隨機實例,這個實例會保證轉(zhuǎn)發(fā)你的查詢到正確的節(jié)點。Redis 集群在客戶端的幫助下,實現(xiàn)了查詢路由的一種混合形式 (請求不是直接從 Redis 實例轉(zhuǎn)發(fā)到另一個,而是客戶端收到重定向到正確的節(jié)點)。

分片的缺點(Disadvantages)

Redis 的一些特性與分片在一起時玩轉(zhuǎn)的不是很好:

  • 涉及多個鍵的操作通常不支持。例如,你不能對映射在兩個不同 Redis 實例上的鍵執(zhí)行交集(事實上有辦法做到,但不是直接這么干)。
  • 涉及多個鍵的事務(wù)不能使用。
  • 分片的粒度(granularity)是鍵,所以不能使用一個很大的鍵來分片數(shù)據(jù)集,例如一個很大的有序集合。
  • 當(dāng)使用了分片,數(shù)據(jù)處理變得更復(fù)雜,例如,你需要處理多個 RDB/AOF 文件,備份數(shù)據(jù)時你需要聚合多個實例和主機的持久化文件。
  • 添加和刪除容量也很復(fù)雜。例如,Redis 集群具有運行時動態(tài)添加和刪除節(jié)點的能力來支持透明地再均衡數(shù)據(jù),但是其他方式,像客戶端分片和代理都不支持這個特性。但是,有一種稱為預(yù)分片(Presharding)的技術(shù)在這一點上能幫上忙。

數(shù)據(jù)存儲還是緩存(Store or cache)

盡管無論是將 Redis 作為數(shù)據(jù)存儲還是緩存,Redis 的分片概念上都是一樣的,但是作為數(shù)據(jù)存儲時有一個重要的局限。當(dāng) Redis 作為數(shù)據(jù)存儲時,一個給定的鍵總是映射到相同的 Redis 實例。當(dāng) Redis 作為緩存時,如果一個節(jié)點不可用而使用另一個節(jié)點,這并不是一個什么大問題,按照我們的愿望來改變鍵和實例的映射來改進系統(tǒng)的可用性(就是系統(tǒng)回復(fù)我們查詢的能力)。

一致性哈希實現(xiàn)常常能夠在指定鍵的首選節(jié)點不可用時切換到其他節(jié)點。類似的,如果你添加一個新節(jié)點,部分?jǐn)?shù)據(jù)就會開始被存儲到這個新節(jié)點上。

這里的主要概念如下:

  • 如果 Redis 用作緩存,使用一致性哈希來來實現(xiàn)伸縮擴展(scaling up and down)是很容易的。
  • 如果 Redis 用作存儲,使用固定的鍵到節(jié)點的映射,所以節(jié)點的數(shù)量必須固定不能改變。否則,當(dāng)增刪節(jié)點時,就需要一個支持再平衡節(jié)點間鍵的系統(tǒng),當(dāng)前只有 Redis 集群可以做到這一點,但是 Redis 集群現(xiàn)在還處在 beta 階段,尚未考慮再生產(chǎn)環(huán)境中使用。

預(yù)分片(Presharding)

我們已經(jīng)知道分片存在的一個問題,除非我們使用 Redis 作為緩存,增加和刪除節(jié)點是一件很棘手的事情,使用固定的鍵和實例映射要簡單得多。

然而,數(shù)據(jù)存儲的需求可能一直在變化。今天我可以接受 10 個 Redis 節(jié)點(實例),但是明天我可能就需要 50 個節(jié)點。

因為 Redis 只有相當(dāng)少的內(nèi)存占用(footprint)而且輕量級(一個空閑的實例只是用 1MB 內(nèi)存),一個簡單的解決辦法是一開始就開啟很多的實例。即使你一開始只有一臺服務(wù)器,你也可以在第一天就決定生活在分布式的世界里,使用分片來運行多個 Redis 實例在一臺服務(wù)器上。

你一開始就可以選擇很多數(shù)量的實例。例如,32 或者 64 個實例能滿足大多數(shù)的用戶,并且為未來的增長提供足夠的空間。

這樣,當(dāng)你的數(shù)據(jù)存儲需要增長,你需要更多的 Redis 服務(wù)器,你要做的就是簡單地將實例從一臺服務(wù)器移動到另外一臺。當(dāng)你新添加了第一臺服務(wù)器,你就需要把一半的 Redis 實例從第一臺服務(wù)器搬到第二臺,如此等等。

使用 Redis 復(fù)制,你就可以在很小或者根本不需要停機時間內(nèi)完成移動數(shù)據(jù):

  • 在你的新服務(wù)器上啟動一個空實例。
  • 移動數(shù)據(jù),配置新實例為源實例的從服務(wù)。
  • 停止你的客戶端。
  • 更新被移動實例的服務(wù)器 IP 地址配置。
  • 向新服務(wù)器上的從節(jié)點發(fā)送 SLAVEOF NO ONE 命令。
  • 以新的更新配置啟動你的客戶端。
  • 最后關(guān)閉掉舊服務(wù)器上不再使用的實例。

Redis 分片的實現(xiàn)(Implementations)

到目前為止,我們從理論上討論了 Redis 分片,但是實踐情況如何呢?你應(yīng)該使用什么系統(tǒng)呢?

Redis 集群(Redis Cluster)

Redis 集群是自動分片和高可用的首選方式。當(dāng)前還不能完全用于生產(chǎn)環(huán)境,但是已經(jīng)進入了 beta 階段,所以我們推薦你開始小試牛刀。你可以從集群教程(請持續(xù)關(guān)注本公眾賬號后續(xù)文章,譯者注)中獲取更多 Redis 集群的相關(guān)信息。

一旦 Redis 集群可用,以及支持 Redis 集群的客戶端可用,Redis 集群將會成為 Redis 分片的事實標(biāo)準(zhǔn)。

Redis 集群是查詢路由和客戶端分片的混合模式。

Twemproxy

Twemproxy 是 Twitter 開發(fā)的一個支持 Memcached ASCII 和 Redis 協(xié)議的代理。它是單線程的,由 C 語言編寫,運行非常的快。他是基于 Apache 2.0 許可的開源項目。

Twemproxy 支持自動在多個 Redis 實例間分片,如果節(jié)點不可用時,還有可選的節(jié)點排除支持(這會改變鍵和實例的映射,所以你應(yīng)該只在將 Redis 作為緩存是才使用這個特性)。

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

從根本上說,Twemproxy 是介于客戶端和 Redis 實例之間的中間層,這就可以在最下的額外復(fù)雜性下可靠地處理我們的分片。這是當(dāng)前我們建議的處理 Redis 分片的方式。你可以閱讀更多關(guān)于 Twemproxy 的信息(作者的這篇博客文章 http://antirez.com/news/44,譯者注)。

支持一致性哈希的客戶端

Twemproxy 之外的可選方案,是使用實現(xiàn)了客戶端分片的客戶端,通過一致性哈?;蛘邉e的類似算法。有多個支持一致性哈希的 Redis 客戶端,例如 Redis-rb 和 Predis。

請查看完整的 Redis 客戶端列表,看看是不是有支持你的編程語言的,并實現(xiàn)了一致性哈希的成熟客戶端。