redis集群模式为什么使用槽的方式?

2023/07/07 Redis 共 2576 字,约 8 分钟

两个问题:

  • Redis Cluster为什么不采用把key直接映射到实例的方式,而采用哈希槽的方式?
  • 为什么不采用一致性hash,而是用这种槽的方式?

正文

Redis Cluster为什么不采用把key直接映射到实例的方式,而采用哈希槽的方式?

Redis Cluster不采用把key直接映射到实例的方式,而采用哈希槽的方式原因:

1、整个集群存储key的数量是无法预估的,key的数量非常多时,直接记录每个key对应的实例映射关系,这个映射表会非常庞大,这个映射表无论是存储在服务端还是客户端都占用了非常大的内存空间。

2、Redis Cluster采用无中心化的模式(无proxy,客户端与服务端直连),客户端在某个节点访问一个key,如果这个key不在这个节点上,这个节点需要有纠正客户端路由到正确节点的能力(MOVED响应),这就需要节点之间互相交换路由表,每个节点拥有整个集群完整的路由关系。如果存储的都是key与实例的对应关系,节点之间交换信息也会变得非常庞大,消耗过多的网络资源,而且就算交换完成,相当于每个节点都需要额外存储其他节点的路由表,内存占用过大造成资源浪费。

3、当集群在扩容、缩容、数据均衡时,节点之间会发生数据迁移,迁移时需要修改每个key的映射关系,维护成本高。

4、而在中间增加一层哈希槽,可以把数据和节点解耦,key通过Hash计算,只需要关心映射到了哪个哈希槽,然后再通过哈希槽和节点的映射表找到节点,相当于消耗了很少的CPU资源,不但让数据分布更均匀,还可以让这个映射表变得很小,利于客户端和服务端保存,节点之间交换信息时也变得轻量。

5、当集群在扩容、缩容、数据均衡时,节点之间的操作例如数据迁移,都以哈希槽为基本单位进行操作,简化了节点扩容、缩容的难度,便于集群的维护和管理。

redis集群模式为什么不采用一致性hash,而是用这种槽的方式?

一致性hash通常用来解决当集群节点数变化时,大量缓存无法命中的情况,因为hashcode/节点数,节点数的变化使得大部分计算结果都发生了变化。一致性hash可以保证同一个key在节点数变化的时候,大部分“hashcode/节点数”仍然能计算出相同结果。

而redis的槽设计,将节点数设定为16384,每个节点分配一定的槽数,客户端也可以缓存槽与节点的对应关系,节点与节点之间也知道槽与节点的对应关系。槽的设计就像是客户端与redis集群的一个中间层,它将集群中存储空间的分配具象化了。你可以让节点A多分配几个槽,以增大节点A承受的压力,也可以对节点B少分配几个槽,以降低节点B承受的压力,当然,一致性hash也可以实现,但没有槽来得这么具体。

想象一下没有槽的设计,客户端需要去服务器获取一个key,首先需要计算key的hash值,然后获取hash环,定位key所在的redis节点(如果需要增加平衡度,可能还需要引入虚拟节点,根据虚拟节点再找真实节点)。在这个过程中,槽比hash环的设计,性能是更高的,同时在压力分配问题上也更加灵活。

而且一致性哈希的节点分布基于圆环,无法很好的手动控制数据分布,比如有些节点的硬件差,希望少存一点数据,这种很难操作(还得通过虚拟节点映射,总之较繁琐)。 而redis集群的槽位空间是可以用户手动自定义分配的,类似于 windows 盘分区的概念,可以手动控制大小。

我的结论是:都可以,但槽更加合适。不管是性能上,还是可维护性,灵活性都更高。

个人思考

补充一下Redis集群相关的知识,以及我的理解:

Redis使用集群方案就是为了解决单个节点数据量大、写入量大产生的性能瓶颈的问题。多个节点组成一个集群,可以提高集群的性能和可靠性,但随之而来的就是集群的管理问题,最核心问题有2个:请求路由、数据迁移(扩容/缩容/数据平衡)。

1、请求路由:一般都是采用哈希槽的映射关系表找到指定节点,然后在这个节点上操作的方案。 Redis Cluster在每个节点记录完整的映射关系(便于纠正客户端的错误路由请求),同时也发给客户端让客户端缓存一份,便于客户端直接找到指定节点,客户端与服务端配合完成数据的路由,这需要业务在使用Redis Cluster时,必须升级为集群版的SDK才支持客户端和服务端的协议交互。 其他Redis集群化方案例如Twemproxy、Codis都是中心化模式(增加Proxy层),客户端通过Proxy对整个集群进行操作,Proxy后面可以挂N多个Redis实例,Proxy层维护了路由的转发逻辑。操作Proxy就像是操作一个普通Redis一样,客户端也不需要更换SDK,而Redis Cluster是把这些路由逻辑做在了SDK中。当然,增加一层Proxy也会带来一定的性能损耗。

2、数据迁移:当集群节点不足以支撑业务需求时,就需要扩容节点,扩容就意味着节点之间的数据需要做迁移,而迁移过程中是否会影响到业务,这也是判定一个集群方案是否成熟的标准。

Twemproxy不支持在线扩容,它只解决了请求路由的问题,扩容时需要停机做数据重新分配。而Redis Cluster和Codis都做到了在线扩容(不影响业务或对业务的影响非常小),重点就是在数据迁移过程中,客户端对于正在迁移的key进行操作时,集群如何处理?还要保证响应正确的结果?

Redis Cluster和Codis都需要服务端和客户端/Proxy层互相配合,迁移过程中,服务端针对正在迁移的key,需要让客户端或Proxy去新节点访问(重定向),这个过程就是为了保证业务在访问这些key时依旧不受影响,而且可以得到正确的结果。由于重定向的存在,所以这个期间的访问延迟会变大。等迁移完成之后,Redis Cluster每个节点会更新路由映射表,同时也会让客户端感知到,更新客户端缓存。Codis会在Proxy层更新路由表,客户端在整个过程中无感知。

除了访问正确的节点之外,数据迁移过程中还需要解决异常情况(迁移超时、迁移失败)、性能问题(如何让数据迁移更快、bigkey如何处理),这个过程中的细节也很多。

Redis Cluster的数据迁移是同步的,迁移一个key会同时阻塞源节点和目标节点,迁移过程中会有性能问题。而Codis提供了异步迁移数据的方案,迁移速度更快,对性能影响最小,当然,实现方案也比较复杂。

文档信息

Search

    Table of Contents