本篇文章主要介绍redis cluster分区实现的原理,介绍一些redis集群常见到的概念,帮助对redis有了解的兄die更好的理解redis,大佬勿喷。

一、redis cluster特点:

1、所有的节点之间都是联通的

2、集群消息通信通过集群总线通信,集群总线端口大小为客户端服务端口+10000,这个10000是固定值;

3、节点之间的通信协议采用二进制的方式;

4、客户端和集群节点之间通信和通常一样,通过文本协议进行;

5、集群节点不会代理查询,也就是某个key不存在当前查询的节点时,会返回该key对应的slot值。

二、分区实现原理

关于redis我们听过一致性hash,但是在redis cluster实现中,我们不关心一致性hash算法,或者说一致性hash是在redis3.0之前我们经常讨论的,一致性hash保证了增加或减少节点时尽可能少的移动数据。

redis-cluster的分区实现,是将数据分散到不同的节点上,这里会有一个solt(槽)的概念,每一个集群中的master会“持有”一段solt,slot跟key之间有映射,这里的slot是一个虚拟的概念。举个栗子,如果有个6各节点,三主三从的redis cluster,这个集群中有1000个key,那么这1000个的分布每个master上保存333个,会有一个master上保存334个,当然这是理想的状态,实际可能会有所出入。

slot(槽)

redis cluster默认有16384个槽,这里的槽是个虚拟的概念,在redis cluster中只有master拥有槽,slave只有使用权利,不能拥有槽,redis集群创建的时候(一般创建都是用redis-trib.rb脚本),会按照默认的形式进行槽分配,这里每个master拥有的槽也不是固定不变的,可以通过redis-trib.rb的move slot进行槽的分配。这些槽是由一个16384/8字节的位序列结构维护,同时集群维护者slot到节点的映射,这个映射关系通过长度为16384的数组实现,数组的下标为slot编号,数据内容为集群的节点,通过这个数组,集群可以很快的找到负责这个槽的节点。

键空间分布

键到slot的映射关系通过算法HASH_SLOT = CRC16(key) mod 16384 实现,这个算法可以使key与slot的映射保持唯一。

slot跟key并不是一对一的,拥有16384个槽,不等用集群只能存储16384个key,可以通过CLUSTER COUNTKEYSINSLOT <slot>命令查看某个slot中所有的key。

例如一次提交了10个key到一个拥有三个master的cluster 中,根据建空间分布基本算法,将这10个key分不到不同的slot中,不同的slot有不同的node节点服务,就实现了key的分布存储。

键哈希标签原理

通过上面的算法我们可以实现key的分布存储,但是可能跟我希望的不一样,加入我有一批key,同属一个业务,我希望这些key是存在同一个node节点上的,但是根据上面的实现我们很难保证会存储在同一个node节点上,怎么实现呢?

键哈希标签原理可以实现这种需求,我们在定义key时,我们可以指定特定的标签。如:abc{userid}12344,def{userid}12345,这两个可以在计算slot编号时,这回获取{}中间的字符进行槽编号的计算。这样就能保证他们落在同样的slot中。

重定向客户端

如果我们连接redis cluster的node,执行get key命令,会有两种情况,如果这个key在当前节点,则返回该key对应的value值,如果当前节点不存在key,则返回-MOVED <slot> host:port,可以通过命令cluster keyslot key查看该key代对应的slots序号。

ps:我们使用中查询数据的时候并没有遇到过-MOVED这种情况,这又是为什么呢?这是应为我们平常用到的redis客户端,如java客户端jedis,python客户端redis-cluster-py都为我们实现了重定向。

当集群设置了readonly后,如果slave节点收到请求,请求的key并不在slave节点时,并不会将请求moved,而是自己处理,这将是slave的readonly模式,通过readwrite命令,可以将slave的readonly模式重置。