数据结构论坛

首页 » 分类 » 定义 » Redis专题详解RedisClust
TUhjnbcbe - 2021/8/14 16:46:00

通过上一节的内容,我们已经知道了RedisCluster结构、设计理念以及从无到有创建一个集群,总体上来讲对于RedisCluster有了一个初步的认识。本节将重点解析RedisCluster数据分片的更多细节,帮助大家更好的理解与使用。

数据分片机制数据分片

不同于单机版Redis及Sentinel模式中一个节点负责所有key的管理工作,RedisCluster采用了类似于一致性哈希算法的哈希槽(hashslot)机制、由多个主节点共同分担所有key的管理工作。

RedisCluster使用CRC16算法把key空间分布在个哈希槽内,哈希槽是按照序号从0~标号的,每组主从节点只负责一部分哈希槽管理操作;而且通过集群状态维护哈希槽与节点之间的映射关系,随着集群运行随时更新。如上面我们示例中,哈希槽与节点关系如下:

Master[0]负责Slots:0-Master[1]负责Slots:-Master[2]负责Slots:-

每当我们通过RedisCluster对某个key执行操作时,接收请求的节点会首先对key执行计算,得到该key对应的哈希槽,然后再从哈希槽与节点的映射关系中找到负责该哈希槽的节点。如果是节点自身,则直接进行处理;如果是其他节点,则通过重定向告知客户端连接至正确的节点进行处理。

HASH_SLOT=CRC16(key)mod

由于数据分片机制的存在,不同的key可能存储在不同的节点上,这就导致普通Redis中的一些多key之间的计算命令无法支持。因为key不同,其对应的哈希槽可能不同,导致这些数据存储在不同的节点上,如果一个命令涉及到多个节点的key,性能较低。所以,RedisCluster实现了所有在普通Redis版本中的单一key的命令,那些使用多个key的复杂操作,比如set的union、intersection操作只有当这些key在同一个哈希槽时才可用。

但是,实际应用中,我们确实存单个命令涉及多个key的情况,基于此问题RedisCluster提供了哈希标签在一定程度上满足使用需求。

哈希标签

RedisCluster提供了哈希标签(HashTags)来强制多个key存储到同一个哈希槽内,哈希标签通过匹配key中“{”、“}”之间的字符串提取真正用于计算哈希槽的key。比如:客户端输入{abcd}test,那么将只把abcd用于哈希槽的计算;这样{abcd}test、{abcd}prod就会被存储到同一个哈希槽内。但是,客户端输入的key可能存在多个“{”或“}”,此时RedisCluster将会如下规则处理:

key中存在“{”字符,并且“{”的右侧存在“}”;“{”与“}”之间存在一个或多个字符;

满足以上两个条件,RedisCluster将把“{”与“}”之间的内容作为真正的key进行哈希槽计算,否则还是使用原来的输入执行计算。需要注意:“{”和“}”的匹配遵循最左匹配原则。举例看下:

{user}.following和{user}.followers:最终采用user;

foo{}{bar}:最终采用foo{}{bar};

foo{{bar}}zap:最终采用{bar;

foo{bar}{zap}:最终采用bar;

重新分片

当集群中节点压力过大时,我们会考虑通过扩容,让新增节点分担其他节点的哈希槽;当集群中节点压力不平衡时,我们会考虑把部分哈希槽从压力较大的节点转移至压力较小的节点。

RedisCluster支持在不停机的情况下添加或移除节点,以及节点间哈希槽的迁出和导入,这种动态扩容或配置的方式对于我们的生产实践好处多多。比如:电商场景中,日常流量比较稳定,只要按需分配资源确保安全水位即可;当遇到大促时,流量较大,我们可以新增资源,以不停机、不影响业务的方式实现服务能力的水平扩展。以上两种情况我们称之为重新分片(Resharding)或者在线重配置(LiveReconfiguration),我们来分析下Redis是如何实现的。

通过前面了解集群状态的数据结构,我们知道哈希槽的分配其实是一个数组,数组索引序号对应哈希槽,数组值为负责哈希槽的节点。理论上,哈希槽的重新分配实质上是根据数组索引修改对应的节点对象,然后通过状态传播在集群所有节点达到最终一致。如下图中,把负责哈希槽的节点从修改为。实际中,为了实现上面的过程,还需要考虑更多方面。

我们知道,哈希槽是由key经过CRC16计算而来的,哈希槽只是为了把key存储到真正节点时一个虚拟的存在,一切的操作还得回归到key上。当把哈希槽负责的节点从旧节点改为新节点时,需要考虑旧节点存量key的迁移问题,也就是要把旧节点哈希槽中的key全部转移至新的节点。

但是,无论哈希槽对应多少个key,key中存储了多少数据,把key从一个节点迁移至另外一个节点总是消耗时间的,同时需要保证原子性;而且,重新分片过程中,客户端的请求并没有停止,Redis还需要正确响应客户端请求,使之不受影响。

接下来,我们利用示例集群做一次重新分片的实践,并且结合源码深入剖析一下Redis的实现过程。以下示例是把节点的两个哈希槽迁移至节点,过程简述如下:

使用命令redis-cli--clusterreshard.0.0.1:对集群发起重新分片的请求;redis-cli输出集群当前哈希槽分配情况后,询问迁移哈希槽的数量Howmanyslotsdoyouwanttomove(from1to)?,输入数字2,回车确认;redis-cli询问由哪个节点接收迁移的哈希槽:WhatisthereceivingnodeID?,输入节点的ID,回车确认;redis-cli询问迁移哈希槽的来源:输入all代表从其他所有节点中均分,逐行输入节点ID以done结束代表从输入节点迁移哈希槽,这里我输入了的节点ID;redis-cli输出本次重新分片的计划,源节点、目标节点以及迁移哈希槽的编号等内容;输出yes确认执行,输入no停止;输入yes后,redis-cli执行哈希槽迁移;

执行过程截图如下:

以上过程对应的源码为文件redis-cli.c中clusterManagerCommandReshard函数,代码比较多,我们

1
查看完整版本: Redis专题详解RedisClust