Redis——集群 📅 2026/7/6 13:47:18 集群是什么主从结构解决的是可用性和并发量的问题集群解决的是内存容量不足的问题简单来讲就是把所有数据分成几个部分分别存放在集群中的不同分片上。示意图如下数据分片算法数据分片算法决定如何把数据映射到不同的分片上哈希求余数据所在的分片编号 hash(数据的key)%分片的总个数其中hash是指把任意类型值转成数字的hash函数优点简单高效数据均匀分配缺点如果要扩容的话增加分片大部分数据要重新映射搬运工作量巨大一致性哈希算法假设hash值的范围是0 — 2^32-1。可以把这个范围分成n段也就是n个分片数据的key的hash值属于哪个分片范围就把它存到哪个分片里如果要扩容的话就是将原有分片再分片这样一来扩容的搬运成本就更低了只需要将分片0中部分范围的数据搬运到分片3即可优点降低了搬运成本缺点数据不均匀分布分片1,2范围大分片0,3范围小哈希槽分区算法实际使用一致性哈希算法就是因为新分片逮住一个就分片切割才造成了数据分布部均匀。哈希槽分区算法是在一致性哈希算法思想的基础上把范围变成具体的一个个槽以此来解决扩容后数据不均匀分布的问题总共设置16384个槽n个分片都平分这1638个槽。某个数据被分配到的槽的编号 hash(数据的key)%16384这个槽属于哪个分片数据就被存到哪个分片每个分片使用16384个bit位表示自己包含哪些槽假设要扩容成4个分片只需要每个分片都匀出一些槽给新分片即可这样各个分片的数据仍旧是均匀的可以有16384个分片吗如果一个分片只对应一个槽那么某分片闲置的概率就会加大数据不均匀分布的概率就会加大。而且分片数目太多的话对于整个集群的维护也是一个难题。所以redis作者建议分片个数不要超过1000。为什么是16384个槽位节点之间通过心跳包进行通信心跳包中需携带该节点所负责的槽位slot信息。这些槽位通常使用位图数据结构来表示。一方面以 Redis 集群为例其槽位总数固定为 1638416K对应的位图大小为 2KB。若槽位数增加到 6553664K则位图体积将扩大至 8KB。虽然 8KB 对内存而言微不足道但在高频的网络心跳包中频繁传输这样的数据仍会带来不小的开销。另一方面Redis 集群官方建议分片数不超过 1000 个。因此16384 个槽位对于最大 1000 个分片的场景已足够使用集群的使用集群构建redis-cli --cluster create 172.30.0.101:6379 172.30.0.102:6379 172.30.0.103:6379 172.30.0.104:6379 172.30.0.105:6379 172.30.0.106:6379 172.30.0.107:6379 172.30.0.108:6379 172.30.0.109:6379 --cluster-replicas 2 //cluster-replicas表明该集群中每个分片有几个从节点集群构建成功后客户端可以连接集群中的任意一个节点但是客户端对节点的所有操作都相当于对整个集群的操作因为集群是一个整体。需要注意的是如果客户端连接了某一分片但是这个客户端存取的key没有映射在这个分片所维护的任何一个槽里那么客户端就会收到命令失败的提示。那么客户端难道要每次先确认key在哪个分片才能发送命令吗事实上在登录客户端的时候加上-c选项就可以让客户端命令执行失败后自动重定向到可以执行该命令的分片上。这依赖于每个分片都存着其他分片的信息。理论上集群无法很好支持同时操作多个key的命令因为多个key不一定在同一个分片集群的故障处理集群具有类似于哨兵的功能一但某个分片的主节点瘫痪就会让该分片的从节点晋升为主节点主节点恢复后自动变为新主节点的从节点。故障判定在Redis集群中所有节点都会周期性地通过心跳包进行通信注意是所有整个集群中的每个节点不论主从都会包含完整的信息都会收发心跳包。具体机制如下心跳交互节点A向节点B发送ping包B收到后必须回复pong包。ping和pong仅在消息类型message type属性上不同其余部分完全相同都包含集群配置信息例如节点ID、所属分片、主从角色、从属关系以及所持有的槽位slots位图等。随机发送策略每个节点每秒只会向部分随机节点发起ping包而不是向所有节点广播。这样设计是为了避免节点数量增多时心跳包数量爆炸式增长——若对全部节点发送通信量将按节点数的平方级增长严重影响网络开销。主观下线PFAIL当节点A向B发送ping后若B未能如期响应A会尝试重置与B的TCP连接。若重连依然失败A便将B标记为PFAIL状态即主观下线。信息扩散Gossip协议A判定B为PFAIL后会通过Redis内置的Gossip协议与其他节点沟通向它们确认B的状态。每个节点都维护自己的“下线列表”由于视角不同各节点的列表可能不一致。客观下线FAIL若A发现集群中超过半数的节点也都认为B处于PFAIL状态那么A就会将B正式标记为FAIL客观下线并将这一消息同步给集群中的其他节点其他节点收到后也会将B标记为FAIL。至此B被彻底认定为故障节点。集群宕机条件某些节点宕机可能引发整个集群进入fail状态以下三种情况会导致集群宕机某个分片的所有主节点和从节点全部失效某个分片的主节点挂了且该分片没有从节点可用超过半数的主节点同时宕机。核心原则是必须保证每个槽位slot都能正常工作即每个分片至少要有一个可用的主节点否则就算是整个集群都瘫痪了故障迁移当B被判定为FAIL后集群会根据B的角色采取不同处理若B是从节点则无需进行故障迁移若B是主节点则其下属的从节点例如C和D将触发故障迁移流程即从从节点中提拔一个新的主节点继续为集群提供服务。故障迁移的具体步骤如下参选资格判定从节点首先检查自己是否有竞选资格。如果该从节点与主节点失联时间过长超过阈值说明其数据与主节点差异过大则失去竞选资格。休眠排序具备资格的从节点如C和D会各自休眠一段时间休眠时长计算公式为500ms基础时间 [0, 500ms]随机时间 排名 × 1000ms其中“排名”根据从节点复制主节点的偏移量offset决定offset值越大排名越靠前即排名数值越小因此数据越新的从节点休眠时间越短。拉票竞选休眠时间先到的节点比如C醒来后会向集群中所有其他节点发起拉票请求但只有主节点拥有投票权。投票与晋升每个主节点只有一票。当C获得的票数超过集群中主节点总数的一半时C即当选为新的主节点。随后C执行slaveof no one将自己变为主节点并让其他从节点如D执行slaveof C转而复制C。信息同步C将自身晋升为主节点的消息同步给集群中的所有节点各节点收到后更新自己保存的集群拓扑结构。上述选举过程基于Raft算法这是一种在分布式系统中广泛使用的一致性算法。借助随机休眠时间的机制通常谁先唤醒谁就能率先发起拉票并竞选成功从而高效完成故障恢复。还可以看出实际上集群中的每个节点不论主从貌似有有相同的管理功能比如重定向检测故障等这是集群的去中心化思想这样的好处就是可用性很高并且扩展性很好不需要重新部署配置什么的因为大家都是同等地位。集群扩容redis-cli --cluster add-node 172.30.0.110:6379 172.30.0.101:6379 redis-cli --cluster add-node 172.30.0.111:6379 172.30.0.101:6379 --cluster-slave --cluster-master-id [集群中某节点的 nodeId] //add-node 后的第⼀组地址是新节点的地址. 第⼆组地址是集群中的任意节点地址表示集群本身 //--cluster-slave --cluster-master-id 指定把新节点作为哪个主节点的从节点填加节点到集群后节点自动成为一个master主节点但这个分片默认没有任何的槽所以需要执行下述命令进行槽的重新分配和数据的搬迁redis-cli --cluster reshard 172.30.0.101:6379