Redis 集羣模式說明

Redis 集羣模式分析

  • Cluster & Sharded說明

Cluster & Sharded說明

基礎概念:

  • 數據分佈的控制權 歸屬上可分爲:Server端模式、Client端模式(自己起的名字,用於表意)

1. Cluster模式 + 主從模式

Cluster模式爲 Server端模式,及數據分佈由Server控制

設計基本思路

在原有模型(Data <—> Instance)上抽象出中間層 Slot,即:Data <—> Slot <—> Instance,明顯可知演化出了兩種對應關係

  • Data 與 Slot 之間的映射:通過算法進行對應:CRC16(key) & (16384 - 1)
  • Slot 與 Instance 之間的映射:預分配(Range),集羣默認虛擬化出16284個Slot
    • Server:默認情況,將集羣分成16384個Slot,並在啓動集羣時,爲每個節點分配一段Range的Slot
    • Client:連接集羣中的各節點,並通過“slots”命令獲取各節點對應的Slot Range,並進行cache

請求簡化過程

  1. Client連接集羣,獲取各節點Slot分佈情況,並於存儲於SlotMappingCache中
  2. Client對Data(key, value)發起set請求
    2.1. 首先對key進行 slot映射 計算(CRC16(key) & (16384 - 1)
    2.2. 然後通過SlotMappingCache查詢該slot對應的Instance,並獲取connection
    2.3. 發送set請求
    2.3.1. 響應 – OK – done
    2.3.2. 響應:JedisRedirectionException,刷新SlotMappingCache,並重復2.1、2.2、2.3的流程(由此可知,Server端有對key是否落在本Instance slots的校驗邏輯)

其他說明

  • 集羣狀態
    • Cluster中各節點是通過廣播來實現信息(如可用性等)同步的,並通過一致投票的方式來決定節點是否處於Failure狀態,並採取後續行動。即沒有類似ZK這樣的統一管理角色(Sentinel這裏不進行說明)
    • 當出現一下情況時,Redis Cluster將進去Failure狀態
      • 某個Range完全不提供服務(Master + Slave 都不能提供服務)
      • 半數以上的Master不能提供服務
  • Server端模式
    • Cluster模式爲Server端模式,即可以通過Server來調整數據的分佈情況,例如addNode、delNode、reShard等,都將改變數據的分佈情況
    • 在Cluster模式下,當數據分佈發生變化後,Client通過JedisRedirectionException可以感知到數據分佈的變化,並同步最新的數據分佈映射關係
    • 可能正是因爲這方面的顧慮,所以JedisCluster並沒有提供對 分佈式操作 (mget & mset & Pipeline & Transaction)的支持
  • 主從模式
    • 通過Range分區,可以使各節點都參與服務,但是顧及到服務宕機的情況,不得不爲每一臺Master做一個Slave
    • 同步方式,默認爲異步sync,所以Redis官方 Cluster是不能很好的保證數據強一致性的,且主從切換時,可能會帶來數據丟失;可以調整成同步sync,但是會影響服務性能
  • 遺留問題
    • Cluster模式下添加節點後,可能會導致Client無法識別新節點(觀看源碼後的猜測)
      • 在發生Redirection異常時進行的renewClusterSlots中,並沒有嘗試通過命令來獲取集羣中的成員,即所有成員只包括初始化的那批,因此,可能無法識別到新節點
    • slot重分配之後,數據的轉移操作由誰負責?Server端?

2. Sharded模式 + 主從模式

Sharded模式爲 Client端模式,及數據分佈由Client控制

設計基本思路

Sharded模式採用的是ConsistentHash算法。

如果說Cluster模式是 “數據落槽” 的話,那麼Sharded模式則是 “數據找槽”
因爲Cluster模式下,對數據的Slot計算將會得到一個明確的Slot;但是在Sharded中,對數據的計算只能得到該數據對應的Index,接着還需要通過Index按照一定的方向去尋找離得最近的Slot。

在網上,針對ConsistentHash算法的介紹已經是一片一片了,這裏我簡單展示一下源碼,不細聊該算法。

public class Sharded<R, S extends ShardInfo<R>> 

  private final Hashing algo; // MD5 算法
  
  private TreeMap<Long, S> nodes; // Index與ShardInfo的映射關係,使用有序映射結構 -- TreeMap
  private final Map<ShardInfo<R>, R> resources = new LinkedHashMap<ShardInfo<R>, R>(); // ShardInfo與Jedis的映射關係

  public Sharded(List<S> shards, Hashing algo, Pattern tagPattern) {
    this.algo = algo;
    this.tagPattern = tagPattern;
    initialize(shards);
  }

  private void initialize(List<S> shards) {
    nodes = new TreeMap<Long, S>();

    for (int i = 0; i != shards.size(); ++i) {
      // 獲取一個節點
      final S shardInfo = shards.get(i);
      // 創建160個虛擬Slot,並存儲映射關係至nodes<Long, ShardInfo>中
      for (int n = 0; n < 160; n++) { nodes.put(this.algo.hash("SHARD-" + i + "-NODE-" + n), shardInfo); }
      // 存儲shardInfo與Jedis至resources<ShardInfo, Jedis>
      resources.put(shardInfo, shardInfo.createResource());
    }
  }

  public S getShardInfo(byte[] key) {
    // 計算key以得到index,並根據index獲取大於等於該index的nodes
    SortedMap<Long, S> tail = nodes.tailMap(algo.hash(key));
    // 不存在大於等於index的nodes,返回nodes的第一個元素
    if (tail.isEmpty()) { return nodes.get(nodes.firstKey()); } 
    return tail.get(tail.firstKey()); // 返回大於等於index的nodes中的第一個元素
  }

}

很明顯,Sharded模式下,數據的分佈 由Client控制,即Server端根本不知道自己存的是哪一部分的數據,甚至可以說,Server端根本不知道自己只是存儲了一部分的數據。
既然是這樣,那麼Sharded模式根本就不能正常的進行集羣 擴容。其實 更準確的說,Sharded模式下,集羣不能進行數據分佈的調整。但是 如果能進行 “不調整數據分佈的擴容” 那麼還是可以實現集羣擴容的。

Pre-Sharding設計

爲實現擴容,在Sharded模式上,還需要添加一點要求,即Instance需要小且多(內存小,數量多)。

簡單說明

不調整數據分佈:即不調整每個Instance上所存放數據的範圍。
在這種前提下,並且假設每個Instance的內存上限爲100G,那麼將會是這樣的情況:

  • 如果Instance只有2個,則集羣擴容後的容量上限將爲200G
  • 如果Instance有12個,則集羣擴容後的容量上限將爲1200G
  • 所以Instance的數量,將直接決定集羣容量的上限
  • 同時在集羣建立初期,提供的內存總量將不會太大,如120G,那麼由於Instance數量較多,所以各Instance的內存將較小
擴容過程
  1. 首先假設集羣組成:12個Instance(1 to 12),各Instance大小爲10G,集羣總容量120G;物理節點2個(A、B),即6個Instance分配至一個物理節點。---- A(1 to 6)、B(7 to 12)
  2. 由於生產要求,需要進行集羣擴容,添加一臺物理機器C,則可以做如下規劃:A(1 to 4)、B(7 to 10)、C(5、6、11、12)。
  3. 操作
    3.1. 在C節點上啓動4個Instance(提升maxmemory閾值),並分別設置成5、6、11、12的 從節點
    3.2. 待主從同步完成之後,將 域名代理服務器 進行調整,使Client連接至新的Instance
    3.3. 關閉原5、6、11、12Instance服務,釋放資源
    3.4. 調整其餘各Instance maxmemory參數,實現 動態擴容(config set maxmemory)
  4. 整個集羣中各Instance就此全部擴容完畢
其他說明
  1. 正是因爲Sharded模式下,數據的分佈不會被調整,所以ShardedJedis是支持Pipeline的。但是仍然不支持mget & mset,因爲,它們是一次性發送的請求至服務端的,然而,這些操作中對應的key可能並不分佈在一起
  2. 感覺整體上Sharded模式好像比Cluster模式要好點,但是似乎沒有必要使用ConsistentHash算法(個人感覺)

參考文章

  1. https://www.zhihu.com/question/21419897
  2. https://blog.csdn.net/rosanu_blog/article/details/68066756
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章