Redis源码里头集群到底咋实现的,来看看底层那些细节和原理
- 问答
- 2026-01-06 02:20:09
- 23
数据怎么分?—— 哈希槽(Slot)是核心
Redis集群最大的特点就是把整个数据库分成了16384个“小格子”,每个小格子叫一个哈希槽,或者简称槽(Slot),你可以想象成一个超大的数组,有16384个元素,当你存一个键值对的时候,比如set user:123 "张三",集群会先用一个公式(CRC16算法)算一下这个键user:123的哈希值,然后用这个哈希值对16384取模,得到一个介于0到16383之间的数字,这个数字就决定了你的这个user:123数据应该放在哪个槽里。
这16384个槽又是怎么分配给集群里那些Redis节点的呢?这就是关键了,集群启动时,或者有节点加入退出时,都需要重新分配槽,一个有三个主节点的集群,可能会这么分:节点A管0到5000号槽,节点B管5001到10000号槽,节点C管10001到16383号槽,这个分配信息,被称为集群配置或者路由表,最关键的是,这个路由表每个节点都存了一份完整的,任何一个节点都知道任何一个槽目前归谁管。
第二块:客户端怎么知道去哪访问?—— 重定向机制
现在问题来了,我一个客户端随便连上了集群里的节点A,想读user:123,节点A自己一算,这个键的槽号是3000,而3000号槽正好归它自己管,那没话说,直接处理,返回结果给客户端。
但如果客户端要访问的键是product:456,节点A一算,槽号是12000,这个槽归节点C管,那节点A就不会自己处理这个命令,它会干一件事:给客户端返回一个重定向错误(MOVED错误),这个错误信息非常贴心,它会直接告诉客户端:“这个键不在我这儿,你去连IP为X.X.X.X,端口为Y的节点C吧!”
聪明的客户端收到这个MOVED错误后,不应该简单地报错,而应该根据这个提示,去和正确的节点C建立连接,然后重新发送命令,一些成熟的Redis客户端库(比如Jedis、Lettuce)都已经把这个逻辑封装好了,对开发者来说是透明的,感觉就像在操作一个巨大的Redis一样。
第三块:节点之间怎么通信?—— Gossip协议流言蜚语
集群里这么多节点,怎么知道谁活着谁死了?那个槽的分配信息变了怎么通知大家?Redis节点之间通过一个叫Gossip协议的东西来互相通信,这个名字很形象,流言蜚语”协议。
每个节点都会定期(比如每秒)随机选择几个其他节点,互相发送PING/PONG消息,这些消息里可不止是“我还活着”这么简单,它就像一个八卦小报,里面会携带一些重要的元数据,
- 我自己负责哪些槽。
- 我最近一次和其他节点通信是什么时候,我觉得哪些节点可能下线了。
通过这种随机、一对多的方式传播信息,最终整个集群的所有节点都会在较短时间内达成一致,知道整个集群的完整状态(比如槽的分布、节点的存活状态),这是一种最终一致性的协议,虽然有点延迟,但非常 robust(健壮),没有单点故障。
第四块:主从切换——高可用的保障
光有数据分片还不够,万一有个主节点宕机了,那它负责的那部分数据就完全不可用了,所以Redis集群每个主节点都可以配置一个或多个从节点(副本)。
主节点和从节点之间还是用传统的异步复制来同步数据,所有节点都会通过Gossip协议“八卦”其他节点的状态,当一个主节点被大多数主节点都认为“失联”了之后,这些主节点就会投票,从它的从节点中选出一个新的主节点上来顶替,原来旧主节点负责的那些槽,就划归给这个新主节点,这样,集群就实现了高可用,这个过程是自动的,不需要人工干预。
第五块:一些限制和细节
正是因为这种分片的架构,Redis集群也带来了一些限制,理解了原理就明白为啥有这些限制了:
- 不支持多键操作:像
MSET、SINTER这种需要同时操作多个键的命令,在集群模式下默认是不能用的,因为这几个键很可能被哈希到不同的槽里,分布在不同的节点上,跨节点的事务很难做,除非你用了Hash Tag,就是强制让某些键落在同一个槽里。 - 只能使用0号数据库:单机Redis有16个DB,但集群环境下只使用DB0,这是因为分片逻辑已经通过槽来实现了,不需要再用DB来隔离。
- 复制是异步的:主从复制有延迟,所以在故障切换时,可能会丢失少量数据。
Redis集群的核心就是:用哈希槽分数据,用重定向指导访问,用Gossip协议同步信息,用主从复制保证高可用,它是一个非常精巧的、去中心化的分布式解决方案。
(注:以上实现细节主要参考自Redis源码中的 cluster.c, cluster.h 文件,以及Redis官方文档中关于集群规范的描述。)

本文由芮以莲于2026-01-06发表在笙亿网络策划,如有疑问,请联系我们。
本文链接:https://www.haoid.cn/wenda/75302.html
