系列文章
为什么我们需要Redis Cluster?
前文我们提到的主从复制以及在主从基础上的哨兵机制都是采用的复制这种模式,但如果数据量过大,复制这种方式显然就非常不合理了。这个时候就需要本章的主角,在Redis 3.0
加入的Cluster
分片技术来对单数据库数据进行拆分,拆分为多个节点运行。
Redis Cluster是什么?
Redis Cluster就是将数据Key映射到很多个哈希槽(Hash Slot),然后不同节点负责存储一部分的哈希槽。每次新加入或者需要获取Key时会对Key进行运算,锁定到对应节点后获取。以达到数据分开储存,不让单点数据过大的需求。
需要注意:
- 上图中的每个节点其实还可以拓展自己的子节点以达到高可用
Cluster原理?
哈希槽(Hash Slot)
Redis Cluster引入了哈希槽的概念。Redis Cluster中有16384(即2的14次方)个哈希槽,每个key通过CRC16校验后对16383取模来决定放置哪个槽。Cluster中的每个节点负责一部分hash槽(hash slot)。
Hash tags
有时我们需要将多个key存放如同一个节点内,为了做到这一点,Redis提供了Hash tags这种方式将不同的key分配到相同的hash slot中。原理也比较简单,原本的hash分配是对整个Key进行校验取模,而Hash tags是只对Hash tags进行取模。规则如下:
- key包含一个
{
字符 - 并且 如果在这个
{
的右面有一个}
字符 - 并且 如果在
{
和}
之间存在至少一个字符
举例:
-
{test}A
和B{test}
将会被分到同一个hash slot中
,其中参与校验取模的值为test
。 -
A{}
这个key
不会启用hash tags
,因为括号内没有用来校验取模的值。 -
A{test1}{test2}
会启用hash tags
其中参与校验的值是test1
,test2
则不会参与。
Cluster nodes属性
每个节点在cluster中有一个唯一的名字。这个名字由160bit
随机十六进制数字表示,并在节点启动时第一次获得(通常通过/dev/urandom
)。节点在配置文件中保留它的ID,并永远地使用这个ID,直到被管理员使用CLUSTER RESET HARD
命令hard reset
这个节点。
节点ID被用来在整个cluster中标识每个节点。一个节点可以修改自己的IP地址而不需要修改自己的ID。Cluster可以检测到IP /port
的改动并通过运行在cluster bus
上的gossip
协议重新配置该节点。
节点ID不是唯一与节点绑定的信息,但是他是唯一的一个总是保持全局一致的字段。每个节点都拥有一系列相关的信息。一些信息时关于本节点在集群中配置细节,并最终在cluster内部保持一致的。而其他信息,比如节点最后被ping的时间,是节点的本地信息。
|--------------------------------------------------------------------cluster nodes---------------------------------------------------------------------|
| node id | address:port | flags - last ping sent | last pong received | configuration epoch | link state-slots |
|------------------------------------------|----------------|--------------------------------|--------------------|---------------------|------------------|
| d1861060fe6a534d42d8a19aeb36600e18785e04 | 127.0.0.1:6379 | myself - 0 1318428930 | 1 | connected | 0-1364 |
| 3886e65cc906bfd9b1f7e7bde468726a052d1dae | 127.0.0.1:6380 | master - 1318428930 1318428931 | 2 | connected | 1365-2729 |
| d289c575dcbc4bdd2931585fd4339089e461a27d | 127.0.0.1:6381 | master - 1318428931 1318428931 | 3 | connected | 2730-4095 |
Cluster总线
每个Redis Cluster节点有一个额外的TCP端口用来接受其他节点的连接。这个端口与用来接收client命令的普通TCP端口有一个固定的offset。该端口等于普通命令端口加上10000.例如,一个Redis接口在端口6379
坚挺客户端连接,那么它的集群总线端口16379
也会被打开。
节点到节点的通讯只使用集群总线,同时使用集群总线协议:有不同的类型和大小的帧组成的二进制协议。集群总线的二进制协议没有被公开文档化,因为它不希望被外部软件设备用来预知到群节点。
集群拓扑
Redis Cluster是一张全网拓扑,节点与其他每个节点之间都保持着TCP
连接。 在一个拥有N个节点的集群中,每个节点由N-1个TCP
传出连接,和N-1个TCP
传入连接。 这些TCP
连接总是保持活性(be kept alive)。当一个节点在集群总线上发送了ping
请求并期待对方回复pong
,(如果没有得到回复)在等待足够成时间以便将对方标记为不可达之前,它将先尝试重新连接对方以刷新与对方的连接。 而在全网拓扑中的Redis Cluster节点,节点使用gossip
协议和配置更新机制来避免在正常情况下节点之间交换过多的消息,因此集群内交换的消息数目(相对节点数目)不是指数级的。
节点握手
节点总是接受集群总线端口的链接,并且总是会回复ping
请求,即使ping
来自一个不可信节点。然而,如果发送节点被认为不是当前集群的一部分,所有其他包将被抛弃。
节点认定其他节点是当前集群的一部分有两种方式:
- 如果一个节点出现在了一条
MEET
消息中。一条MEET
消息非常像一个ping
消息,但是它会强制接收者接受一个节点作为集群的一部分。节点只有在接收到系统管理员的如下命令后,才会向其他节点发送MEET
消息:
CLUSTER MEET ip port
- 如果一个被信任的节点
gossip
了某个节点,那么接收到gossip
消息的节点也会那个节点标记为集群的一部分。也就是说,如果在集群中,A知道B,而B知道C,最终B会发送gossip
消息到A,告诉A节点C是集群的一部分。这时,A会把C注册未网络的一部分,并尝试与C建立连接。
这意味着,一旦我们把某个节点加入了连接图(connected graph),它们最终会自动形成一张全连接图(fully connected graph)。这意味着只要系统管理员强制加入了一条信任关系(在某个节点上通过MEET
命令加入了一个新节点),集群可以自动发现其他节点。
Redis Cluster的优势?
- 这个方案依然可以看做对前两种的拓展技术,为的是解决前两种方案无法解决的数据量过大问题。
- 因为采用的是
Hash slot
的方式,增加节点和减少节点不会有大批量的数据迁移。 - 依然可搭配主从使用,让系统高可用也有一个保证。
Redis Cluster的缺点?
- 此技术是为了大数据量做的,如果数据量过小使用此技术相对于单机反而会降低效率。