Redis集羣方案總結

Redis回顧java

Redis支持的數據結構

  • 字符串(String)
  • 哈希(Hash)
  • 列表(List)
  • 集合(Set)
  • 有序集合(Sorted Set)位數組
    • 支持針對score做範圍查詢
  • HyperLogLog
    • 作基數統計的算法

Redis支持的操做

  • 基本操做發佈/訂閱
    • Set get add push pop…
  • Pipeline操做
  • 事務
    • 事務支持不完整。不提供回滾命令。

Redis適用場景

  • KV存儲
  • 緩存(TTL LRU...)消息中間件
    • LRU是redis達到maxmemory以後,可選的key刪除策略。
  • 分佈式鎖

 

Redis集羣實現方式

實現基礎——分區

  • 分區是分割數據到多個Redis實例的處理過程,所以每一個實例只保存key的一個子集。
  • 經過利用多臺計算機內存的和值,容許咱們構造更大的數據庫。
  • 經過多核和多臺計算機,容許咱們擴展計算能力;經過多臺計算機和網絡適配器,容許咱們擴展網絡帶寬。

集羣的幾種實現方式

  • 客戶端分片
  • 基於代理的分片
  • 路由查詢

客戶端分片

  • 由客戶端決定key寫入或者讀取的節點。
  • 包括jedis在內的一些客戶端,實現了客戶端分片機制。

原理以下所示:git

 
 

特性github

  • 優勢
    • 簡單,性能高。
  • 缺點
    • 業務邏輯與數據存儲邏輯耦合
    • 可運維性差
    • 多業務各自使用redis,集羣資源難以管理
    • 不支持動態增刪節點

基於代理的分片

  • 客戶端發送請求到一個代理,代理解析客戶端的數據,將請求轉發至正確的節點,而後將結果回覆給客戶端。
  • 開源方案
    • Twemproxy
    • codis

基本原理以下所示:
redis

 
 

 

特性算法

  • 透明接入Proxy 的邏輯和存儲的邏輯是隔離的。
    • 業務程序不用關心後端Redis實例,切換成本低。
  • 代理層多了一次轉發,性能有所損耗。

路由查詢

  • 將請求發送到任意節點,接收到請求的節點會將查詢請求發送到正確的節點上執行。
  • 開源方案
    • Redis-cluster

基本原理以下所示:spring


 
 

集羣的挑戰

  • 涉及多個key的操做一般是不被支持的。涉及多個key的redis事務不能使用。
    • 舉例來講,當兩個set映射到不一樣的redis實例上時,你就不能對這兩個set執行交集操做。
  • 不能保證集羣內的數據均衡。
    • 分區的粒度是key,若是某個key的值是巨大的set、list,沒法進行拆分。
  • 增長或刪除容量也比較複雜。
    • redis集羣須要支持在運行時增長、刪除節點的透明數據平衡的能力。

 

Redis集羣各類方案原理

Twemproxy

  • Proxy-based
  • twtter開源,C語言編寫,單線程。
  • 支持 Redis 或 Memcached 做爲後端存儲。

Twemproxy高可用部署架構數據庫

 
 

Twemproxy特性

  • 支持失敗節點自動刪除
    • 與redis的長鏈接,鏈接複用,鏈接數可配置
  • 自動分片到後端多個redis實例上支持redis pipelining 操做
    • 多種hash算法:可以使用不一樣的分片策略和散列函數
    • 支持一致性hash,可是使用DHT以後,從集羣中摘除節點時,不會進行rehash操做
    • 能夠設置後端實例的權重
  • 支持狀態監控
  • 支持select切換數據庫

redis的管道(Pipelining)操做是一種異步的訪問模式,一次發送多個指令,不一樣步等待其返回結果。這樣能夠取得很是好的執行效率。調用方法以下:後端

@Test
public void testPipelined() {
    Jedis jedis = new Jedis("localhost");
    Pipeline pipeline = jedis.pipelined();
    long start = System.currentTimeMillis();
    for (int i = 0; i < 100000; i++) {
        pipeline.set("p" + i, "p" + i);
    }
    List<Object> results = pipeline.syncAndReturnAll();
    long end = System.currentTimeMillis();
    System.out.println("Pipelined SET: " + ((end - start)/1000.0) + " seconds");
    jedis.disconnect();
}

Twemproxy不足

  • 性能低:代理層損耗 && 自己效率低下
  • Redis功能支持不完善
    • 不支持針對多個值的操做,好比取sets的子交併補等(MGET 和 DEL 除外)
    • 不支持Redis的事務操做
    • 出錯提示還不夠完善
  • 集羣功能不夠完善
    • 僅做爲代理層使用
    • 自己不提供動態擴容,透明數據遷移等功能
  • 失去維護
    • 最近一次提交在一年以前。Twitter內部已經再也不使用。

 

Redis Cluster

  • Redis官網推出,可線性擴展到1000個節點
  • 無中心架構
  • 一致性哈希思想
  • 客戶端直連redis服務,免去了proxy代理的損耗

Redis Cluster模型

 
 

Redis-cluster原理

  • Hash slot。集羣內的每一個redis實例監聽兩個tcp端口,6379(默認)用於服務客戶端查詢,16379(默認服務端口 + 10000)用於集羣內部通訊。
    • key的空間被分到16384個hash slot裏;
    • 計算key屬於哪一個slot,CRC16(key) & 16384。
  • 節點間狀態同步:gossip協議,最終一致性。節點間通訊使用輕量的二進制協議,減小帶寬佔用。

 

Gossip 算法又被稱爲反熵(Anti-Entropy),熵是物理學上的一個概念,表明雜亂無章,而反熵就是在雜亂無章中尋求一致,這充分說明了 Gossip 的特色:在一個有界網絡中,每一個節點都隨機地與其餘節點通訊,通過一番雜亂無章的通訊,最終全部節點的狀態都會達成一致。每一個節點可能知道全部其餘節點,也可能僅知道幾個鄰居節點,只要這些節能夠經過網絡連通,最終他們的狀態都是一致的,固然這也是疫情傳播的特色。
簡單的描述下這個協議,首先要傳播謠言就要有種子節點。種子節點每秒都會隨機向其餘節點發送本身所擁有的節點列表,以及須要傳播的消息。任何新加入的節點,就在這種傳播方式下很快地被全網所知道。這個協議的神奇就在於它從設計開始就沒想到信息必定要傳遞給全部的節點,可是隨着時間的增加,在最終的某一時刻,全網會獲得相同的信息。固然這個時刻可能僅僅存在於理論,永遠不可達。

 

Redis-cluster請求路由方式

  • Redis-Cluster藉助客戶端實現了混合形式的路由查詢
    查詢路由並不是直接從一個redis節點到另一個redis,而是藉助客戶端轉發到正確的節點。根據客戶端的實現方式,能夠分爲如下兩種:包括Jedis在內的許多redis client,已經實現了對Redis Cluster的支持。
    • dummy client
    • smart client

查詢路由的流程以下所示:數組

 
 

Redis cluster採用這種架構的考慮:緩存

  • 減小redis實現的複雜度
  • 下降客戶端等待的時間。Smart client能夠在客戶端緩存 slot 與 redis節點的映射關係,當接收到 MOVED 響應時,會修改緩存中的映射關係。請求時會直接發送到正確的節點上,減小一次交互。

Redis Cluster特性

  • 高性能
  • 支持動態擴容,對業務透明
  • 具有Sentinel的監控和自動Failover能力

Redis Cluster不足

  • 官方未提供圖形管理工具,運維比較複雜要求客戶端必須支持cluster協議
    • 好比數據遷移,純手動操做,先分配slot,再將slot中對應的key遷移至新的節點。
  • Redis命令支持不完整
    • 對於批量的指令,如mget支持不完整;不支持事務;不支持數據庫切換,只能使用0號數據庫……
  • 集羣管理與數據存儲耦合
    • 好比若是集羣管理有bug,須要升級整個redis。

Codis

  • 豌豆莢開源的proxy-based的redis集羣方案
  • 支持透明的擴/縮容
  • 運維友好同類產品中最全面的redis命令支持
    • GUI監控界面和管理工具

Codis部署拓撲

 
 

Codis數據存儲

  • 數據根據key分佈到1024個slot內
  • Key的slotid計算方法 crc32(key) % 1024
  • 每一個 codis-server 負責一部分數據
  • 好比後端有3組codis-server,每一個server負責的slot範圍能夠這樣設置:
    • group0 0-300
    • group1 301-600
    • group2 601-1023

Codis模塊簡介

  • Codis Server
    • 基於 redis-2.8.21 分支開發。增長了額外的數據結構,以支持 slot 有關的操做以及數據遷移指令。
  • Codis Proxy
    • 客戶端鏈接的 Redis 代理服務, 實現了 Redis 協議。
    • 對於同一個業務集羣而言,能夠同時部署多個 codis-proxy 實例
    • 不一樣 codis-proxy 之間由 codis-dashboard 保證狀態同步。
  • Codis Dashboard
    • 集羣管理工具,支持 codis-proxy、codis-server 的添加、刪除,以及據遷移等操做。
    • 全部對集羣的修改都必須經過 codis-dashboard 完成。
  • Codis Admin
    • 集羣管理的命令行工具。
    • 可用於控制 codis-proxy、codis-dashboard 狀態以及訪問外部存儲。
  • Codis FE
    • 集羣管理界面。
  • Codis HA
    • 爲集羣提供高可用。
    • 依賴 codis-dashboard 實例,自動抓取集羣各個組件的狀態;
    • 會根據當前集羣狀態自動生成主從切換策略,並在須要時經過 codis-dashboard 完成主從切換。
  • Storage
    • 爲集羣狀態提供外部存儲。
    • 目前僅提供了 Zookeeper 和 Etcd 兩種實現,可是提供了抽象的 interface 可自行擴展。

Codis主從切換

  • Codis-HA:自動切換主從的工具。
  • 經過RESTful API從 codis-dashboard 中拉取集羣狀態
  • 默認以 5s 爲週期
  • 該工具會在檢測到 master 掛掉的時候主動應用主從切換策略,向codis-dashboard發送主從切換命令。
  • 對自動主從切換的支持比較弱
    • 主從切換的限制較多,必須在系統其餘組件狀態健康,且距離上次主從同步數據在必定時間間隔內才能夠執行。
    • 多slave場景,提高其中的一個slave爲master,其餘的slave仍然會從舊的master同步數據,須要管理員手動操做。

Codis數據遷移流程

  • 前提:Codis單條數據遷移是原子操做
  • 單條數據遷移過程:
    • 隨機選取指定 slot 中的一個 <K,V>
    • 將這個<K,V>傳輸給另一個 codis-server
    • 傳輸成功後,把本地的這個 <K,V>刪除

遷移過程以下所示:

 
 

遷移過程當中,Codis-dashboard與proxy經過zk通訊,路由表信息所有存放在zk,保證全部proxy的視圖一致。
Codis如何保證數據遷移過程的正確及透明?

  • codis-config 在實際修改slot狀態以前,會確保全部的 proxy 收到這個遷移請求。
  • 遷移過程當中, 若是客戶端請求 slot 的 key 數據,proxy 會將請求轉發到group2上。
    • proxy會先在group1上強行執行一次 MIGRATE key 將這個鍵值提早遷移過來,
    • 而後再到group2上正常讀取

Codis VS Redis

對比參數 Codis Redis-cluster
Redis版本 基於2.8分支開發 >= 3.0
部署 較複雜。 簡單
運維 Dashboard,運維方便。 運維人員手動經過命令操做。
監控 可在Dashboard裏監控當前redis-server節點狀況,較爲便捷。 不提供監控功能。
組織架構 Proxy-Based, 類中心化架構,集羣管理層與存儲層解耦。 P2P模型,gossip協議負責集羣內部通訊。去中心化
伸縮性 支持動態伸縮。 支持動態伸縮
主節點失效處理 自動選主。 自動選主。
數據遷移 簡單。支持透明遷移。 須要運維人員手動操做。支持透明遷移。
升級 基於redis 2.8分支開發,後續升級不能保證;Redis-server必須是此版本的codis,沒法使用新版本redis的加強特性。 Redis官方推出,後續升級可保證。
可靠性 通過線上服務驗證,可靠性較高。 新推出,坑會比較多。遇到bug以後須要等官網升級。
  1. 理論上,redis-cluster的性能更高,單次請求的延時低。另外,通過實測,兩種架構後端單臺redis-server的條件下,TPS基本沒有差異。
  2. Codis的高可用依賴jodis,或者使用LVS進行高可用部署。

咱們公司選擇Codis的緣由

  • Redis-cluster沒有成熟的應用案例
  • Codis支持的命令更加豐富
  • 遷移至redis cluster須要修改現有實現;而遷移到codis沒這麼麻煩,只要使用的redis命令在codis支持的範圍以內,只要修改一下配置便可接入。
  • Codis提供的運維工具更加友好

Codis簡單壓測

單臺Codis Server壓測結果

 
 

三臺Codis-Server的集羣壓測結果

 
 

壓測結論

  • 針對codis集羣壓測過程當中,後端codis server平均CPU佔用約爲70%~80%。
  • 單臺codis的TPS在6w~7w左右,集羣的TPS在17w左右,能夠達到近乎線性擴展的能力
  • 測試期間後臺codis-server的負載沒有跑滿,仍然具備繼續壓測的潛力。
  • 測試期間proxy機器負載低,能夠支撐更多後端codis server實例。

使用codis注意事項

  • 不支持的命令
  • 防止key衝突
    • 建議現有業務接入codis時,加入項目前綴以做區分。
  • 使用spring data redis操做codis時,只能使用RedisTemplate系列接口,Cache系列接口不可用
    • Cache系列的接口使用了redis的事務指令,事務在Codis以及Redis Cluster中均未提供支持。
  • 避免key value巨大的數據
    • 吞吐量提高不明顯
    • 可能形成各codis實例資源佔用不均衡
相關文章
相關標籤/搜索