Redis-分片
分片(partitioning)就是將你的數據拆分到多個 Redis 實例的過程,這樣每一個實例將只包含全部鍵的子集。本文第一部分將向你介紹分片的概念,第二部分將向你展現 Redis 分片的可選方案。html
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 集羣是自動分片和高可用的首選方式。當前還不能徹底用於生產環境,可是已經進入了 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。