六、集群
6.1 基本概念
单机 Redis 受内存与计算资源限制,数据量增长到需要水平扩展时,就要考虑集群方案。
Redis Cluster 把数据分散到多个节点上,每个节点只保存一部分数据,客户端连接任意节点都能完成操作,整体对外仍然像一台 Redis:
搭建集群后,每个节点都持有部分数据与自己的从节点,节点之间通过 gossip 协议交换状态信息,目录结构大致可以理解为“集群 → 分片 → 节点”:
6.2 数据分片算法
数据按什么规则落到哪个分片,是集群设计的核心问题。
三种方案:哈希求余、一致性哈希、哈希槽。
(1)哈希求余
对 key 计算哈希后对分片数取模,例如 3 个分片时,hash(hello) % 3 = 0,键落在 0 号分片:
哈希求余实现直接,但分片数量变化时,绝大多数数据都要重新映射、搬迁到新分片,成本很高。示例中从 3 个分片扩容到 4 个分片,旧分片上的大量数据要靠搬迁重新分布:
(2)一致性哈希
一致性哈希把 2^32 个哈希值组织成一个圆环,节点按哈希值分布在环上,key 沿环顺时针找到第一个节点即为其归属:
key 的归属由它在环上的位置决定,落在哪个分片的管理区间就属于哪个分片:
用一个“取票机”的类比帮助理解:乘客下车点固定,离哪个取票机近就去哪个取票机取票,取票机增减只影响邻近区间:
扩容时,新节点插入环中,只有环上相邻区间的数据需要搬迁,其余分片不受影响:
(3)哈希槽
Redis Cluster 实际采用哈希槽方案:整个键空间固定划分成 16384 个槽位(16384 = 16 × 1024 = 2^14),key 通过 CRC16 计算后对 16384 取模得到槽位编号,再按槽位分配到各个节点:
hash_slot = crc16(key) % 16384槽位的分配由管理员或工具完成,每个节点用位图记录自己持有的槽位。节点增减时,只需要把相关槽位从旧节点迁移到新节点,搬迁粒度从“全部数据”降为“部分槽位”,扩容与缩容都更可控。
6.3 主节点宕机与故障迁移
集群中的主节点可以配置从节点。主节点故障后,它的从节点会尝试提升为新主节点,接管原主节点持有的槽位,继续对外服务。
整体逻辑与哨兵的故障转移类似,只是作用范围从单个主节点扩展到集群内的每个分片。
6.4 集群扩容
扩容时先启动新节点并加入集群,再把一部分槽位从已有节点迁移到新节点,迁移过程中数据逐步搬迁,完成后再更新槽位分配。
相比哈希求余的全量搬迁,哈希槽方案迁移的数据量小得多,扩容对线上服务的影响也更可控。