前言
在Redis集群中,主要涉及两种Hash算法:
- 一致性哈希算法,这种算法在dis Cluster方案中并未得到实现,主要应用于外部代理模式,比如Twemproxy。
- Slot哈希槽算法,它是Cluster的核心算法。
所以,探讨这个问题时,不能只知其一不知其二。在Redis 3.0之前,Redis没有自带的集群方案,那时若要实现Redis的分布式,主要依靠客户端自行完成。一般来说,常用的实现方式就是一致性Hash。
而在Redis 3.0之后,Redis实现了Cluster集群,转而采用了相对更为简洁的Slot槽方式。
接下来,就让我们一步步深入了解其中的变化,以及背后的原因 :👉👉👉
Redis Cluster集群
Redis集群是一种分布式数据库方案,通过服务器分片技术进行数据管理,我们来对它进行一个归纳总结。
哈希槽
集群将数据划分为 16384 (2^14)个槽位(哈希槽),每个Redis服务节点分配了一部分槽位,因为槽位的信息存储于每个节点中,客户端请求的key通过CRC16校验后对16384取模来决定放置哪个槽,这样也就定位到指定的节点中。
上图中 key 【小许】和【code】经过 CRC16 计算后再对哈希槽总个数 16384 取模,得到哈希槽位置分别是在888的节点A上和10924的节点C上面。
🚩 重点:每个节点都会记录哪些槽分配给了自己,哪些槽被分配给了其他节点
增加节点
新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot(槽)到D上,会变成这样:
此时服务A、B、C、D通过分配各自有了对应的哈希槽,新增节点后集群会自动进行哈希槽的重新平均分配,比如上图中四个节点中每个节点的槽位数是:18384 / 4 = 4096。
当然这个你使用命令 【cluster addslots】为每个节点自定义分配槽的数量,这里有个特点,如果我们节点的机器性能有差异,那就可以为性能好的,配置更多槽位,更好的利用机器性能。
减少节点
如果减少一个节点C,redis cluster同样会自动进行槽数量的重新计算分配,然后后变成下面样子:
删除节点C之后,此时服务A、B节点中每个节点的槽位数是:18384 / 2 = 8192
客户端访问节点数据
Redis cluster的主节点各自负责一部分槽,我们来看下来自客户端的请求的key是如何定位到具体的节点,然后返回对应的数据的。
来自Redis-Cli客户端的请求连接到的是集群中的任何一个节点
首先检查当前key是否存在集群中的节点
- • 通过CRC16(key)/ 16384计算出slot
- • 查询负责该slot负责的节点是否存在
在该节点的话就直接就直接返回key对应的结果
不在该节点的话,那么会 MOVED重定向(包含槽位和目标地址)指引客户端转向至正确的节点,并再次发送之前执行的命令
☀️☀️ 相信你也和小许一样觉得这种方式弊端很明显,每次执行命令前都可能现在Redis节点上进行MOVED重定向才能找到要执行命令的节点,额外增加了IO开销。
不过大多数开发语言的Redis客户端都采用 Smart客户端 支持集群协议,让整个访问就更高效。
我们来看下是如何实现的!
smart客户端
开发语言写的Redis客户端都会采用Smart客户端来支持访问集群。
主要是在内部维护哈希槽–节点的映射关系,这样就可以在Smart客户端实现键到节点的查找,避免了再进行MOVED重定向。
不过第一步还是初始化时会选择一个运行节点,初始化槽和节点映射关系。
我们看下图:
上面我们简单讲了下Redis-Cluster中哈希槽和增删节点槽位的转移分配,回归正题。
🚩 为什么Redis是使用哈希槽而不是一致性哈希呢?
Redis使用哈希槽(Hash Slot)而非一致性哈希(Consistent Hashing)主要基于以下几个原因:
- 实现复杂度
- 哈希槽:哈希槽的实现相对简单直观。Redis集群预分好16384个哈希槽,每个键通过CRC16算法计算出哈希值,再对16384取模,结果对应到相应的哈希槽,然后根据槽与节点的映射关系确定数据存储节点。这种方式易于理解和实现,在集群的扩容、缩容以及节点故障处理时,规则明确,操作相对容易。
- 一致性哈希:一致性哈希算法相对复杂,需要维护一个哈希环,节点和数据都映射到这个环上。在节点增加或减少时,需要重新计算环上节点和数据的分布,对算法和数据结构的要求较高,实现过程更为繁琐。
- 数据迁移与均衡性
- 哈希槽:哈希槽能方便地进行数据迁移,实现数据在节点间的均衡分布。在集群扩容时,只需将部分哈希槽及其对应的数据从旧节点迁移到新节点;缩容时,将离开节点的哈希槽和数据迁移到其他节点。通过合理分配哈希槽,可保证数据在各个节点上的分布较为均匀,充分利用集群资源。
- 一致性哈希:一致性哈希在数据均衡性上存在一定挑战。虽然它能保证在节点增减时,受影响的数据量相对较少,但在实际应用中,由于节点在哈希环上的分布不一定均匀,可能导致数据分布不均衡,部分节点负载过高,而部分节点资源利用率低。
- 节点故障处理
- 哈希槽:在节点故障时,哈希槽的处理机制较为直接。如果某个节点故障,Redis集群可以迅速识别,并将该节点负责的哈希槽重新分配到其他正常节点,保证数据的可用性。客户端可以通过集群的元数据信息,快速调整请求的发送方向,几乎不会对业务造成明显影响。
- 一致性哈希:一致性哈希在节点故障时,虽然也能通过哈希环的调整来重新分配数据,但由于一致性哈希的特性,可能会导致大量数据的重新定位和迁移,影响系统性能和稳定性。同时,客户端需要重新计算哈希环上的数据分布,增加了处理的复杂性和延迟。
- 对集群规模的适应性
- 哈希槽:哈希槽的设计对集群规模的适应性强,无论是小规模集群还是大规模集群都能很好地工作。因为哈希槽数量固定,与集群节点数量无关,在集群规模变化时,只需要调整哈希槽与节点的映射关系,不需要对哈希算法本身进行修改。
- 一致性哈希:一致性哈希在面对大规模集群时,哈希环的维护和管理成本会显著增加,节点和数据的映射关系也会变得更加复杂,导致性能下降和管理难度加大。
为什么Redis Cluster哈希槽数量是16384?
我们知道一致性哈希算法是对2的32次方取模,而哈希槽是对2的14次方取模
✏️ Redis作者认为这样做不太值得;并且一般情况下一个redis集群不会有超过1000个master节点,所以16k的槽位是个比较合适的选择。
Redis作者的回答在这里:why redis-cluster use 16384 slots? · Issue #2576 · redis/redis
总结起来主要有以下因素
- • Redis节点间通信时,心跳包会携带节点的所有槽信息,它能以幂等方式来更新配置。如果采用 16384 个插槽,占空间 2KB (16384⁄8);如果采用 65536 个插槽,占空间 8KB (65536⁄8)。
- • Redis Cluster 不太可能扩展到超过 1000 个主节点,太多可能导致网络拥堵。
- • 16384 个插槽范围比较合适,当集群扩展到1000个节点时,也能确保每个master节点有足够的插槽
这也就是为什么哈希槽的数量是16384了!
文章来源: https://study.disign.me/article/202509/3.redis-cluster.md
发布时间: 2025-02-24
作者: 技术书栈编辑