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

猜你喜欢

转载自blog.csdn.net/weixin_34874025/article/details/82960162