Redis 集群搭建实战:哈希分片与故障转移

当业务并发量和数据量一起涨上去,单机 Redis 会同时撞上两个天花板:内存装不下、单线程写不过来。很多人第一反应是”加从库”,但主从复制只能解决读扩展和备份,写压力依然压在唯一的主节点上。Redis 集群(Redis Cluster)才是真正的横向扩展方案——它把数据按”哈希槽”切成 16384 份,分配到多个主节点,每个主节点再挂从节点做高可用。本文从分片原理讲到六节点搭建与扩容缩容,把一条可落地的集群部署路径讲透。

一、核心原理:16384 个哈希槽

Redis 集群不直接按”key 落在哪台机器”管理,而是引入一层抽象——哈希槽(hash slot)。整个 keyspace 被固定切成 16384 个槽,每个 key 通过 CRC16(key) % 16384 算出它属于哪个槽,槽再被分配给集群里的主节点。也就是说,数据路由是”key → 槽 → 节点”两层映射。这种设计的好处是:节点扩缩容时,只需把一部分”槽”从旧节点迁移到新节点,key 本身不用动。

# key 到槽的计算规则(集群内部逻辑,等价于):
slot = CRC16(key) & 0x3FFF        # 0x3FFF = 16383,共 16384 个槽

# 查看某个 key 落在哪个槽、哪台节点
redis-cli -c -h 127.0.0.1 -p 7000 CLUSTER KEYSLOT user:1001
redis-cli -c -h 127.0.0.1 -p 7000 CLUSTER GETKEYSINSLOT 0 10   # 列出槽 0 下的 10 个 key

理解这一点很关键:集群的”分片”是槽级别,不是 key 级别。迁移、均衡都是按槽搬运的,因此哪怕某个 key 特别大,它也只是占一个槽里的一段空间,不会单独成”片”。这和ShardingSphere 的分库分表MongoDB 的分片集群思路一致——都是”先有逻辑分片单位,再映射到物理节点”,区别只在分片单位和路由算法。

二、为什么需要主从 + 故障转移

哈希槽解决了”数据怎么分”,但没解决”节点挂了怎么办”。Redis 集群要求每个主节点至少配一个从节点:主节点负责自己名下的槽的读写,从节点实时复制主节点的数据。当主节点宕机,集群会通过 gossip 协议在从节点中选举出一个新的主节点顶上,把原主节点名下的槽接管过来——这个过程对客户端基本无感知(少数命令在切换瞬间会失败,需要重试)。

最小可用规模是 三主三从(6 个节点):3 个主节点平分 16384 个槽,3 个从节点各跟一个主。少于 3 个主节点时,一旦某个主挂掉,可能凑不齐多数派完成选举,集群会整体拒绝写入。所以生产环境不要为了省机器用”2 主 + 2 从”这类不对称拓扑。

三、六节点搭建:一步一步来

下面用 6 个实例(端口 7000–7005,三主三从)演示。每个节点的 redis.conf 只需打开集群开关并设好端口:

# 每个节点的 redis.conf(端口改为 7000~7005)
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
appendonly yes
daemonize yes
protected-mode no          # 演示环境;生产应配 bind + 密码
requirepass yourStrongPwd  # 生产务必设密码(下面 create 加 -a)

依次启动 6 个实例后,用官方的 redis-cli --cluster create 一次性把它们组建成集群,并自动分配主从与槽:

# 启动 6 个实例
for p in 7000 7001 7002 7003 7004 7005; do redis-server /path/redis-$p.conf; done

# 创建集群:前 3 个为主,后 3 个自动作为对应主的从
redis-cli -a yourStrongPwd --cluster create \
  127.0.0.1:7000 127.0.0.1:7001 127.0.0.1:7002 \
  127.0.0.1:7003 127.0.0.1:7004 127.0.0.1:7005 \
  --cluster-replicas 1

# 检查集群状态(看到 [OK] 3 masters 即成功)
redis-cli -a yourStrongPwd -c -h 127.0.0.1 -p 7000 CLUSTER INFO
redis-cli -a yourStrongPwd -c -h 127.0.0.1 -p 7000 CLUSTER NODES

--cluster-replicas 1 表示每个主配 1 个从,工具会按”先主后从、尽量错开”的原则分配。创建时会打印槽分配方案并让你确认,确认后它会逐个把 16384 个槽切到对应主节点,整个过程通常几秒到几十秒。

四、客户端怎么连:重定向是常态

集群模式下,客户端必须走智能客户端或在 redis-cli-c 参数。因为 key 算出的槽可能不在当前连的节点上,节点会返回 MOVED <slot> <host:port> 告诉客户端”去那台”。智能客户端会缓存槽映射表,直接连对节点;普通单节点客户端遇到 MOVED 会报错,所以别用连单机的方式连集群。

# 正确:加 -c 自动跟随重定向
redis-cli -a yourStrongPwd -c -h 127.0.0.1 -p 7000
127.0.0.1:7000> set user:1 "alice"     # 可能返回 (redirected to 7002)
-> Redirected to slot [8105] located at 127.0.0.1:7002
OK

# Java(Lettuce / Jedis Cluster)只需给初始节点集合,客户端自己发现拓扑
# JedisCluster jedis = new JedisCluster(Set.of(
#     new HostAndPort("127.0.0.1", 7000),
#     new HostAndPort("127.0.0.1", 7001)));

Redis 缓存基础里我们讲过的单节点用法,到了集群下唯一要改的就是”客户端要能处理重定向、要能感知拓扑变化”。如果你用的框架还停留在单节点连接池,升级到集群前一定要换成 Cluster 版客户端,否则 MOVED 会被当成错误抛出。

五、扩容与缩容:迁移的是槽

业务增长要加节点,或机器退役要减节点,集群都不用停。核心命令是 --cluster add-nodereshard(搬槽)、del-node。搬槽时集群会把对应 key 从源节点流式迁移到目标节点,迁移期间这两个槽处于”中间态”,靠 ASK 重定向让客户端短暂访问源节点、再转向目标,对业务基本透明。

# 加入新主节点(先空节点,再分槽给它)
redis-cli -a pwd --cluster add-node 127.0.0.1:7006 127.0.0.1:7000
# 交互式 reshard:指定要迁移的槽数、目标 node-id、源 node-id
redis-cli -a pwd --cluster reshard 127.0.0.1:7000

# 给新主挂从节点
redis-cli -a pwd --cluster add-node 127.0.0.1:7007 127.0.0.1:7000 \
  --cluster-slave --cluster-master-id <新主node-id>

# 缩容:先把该主所有槽 reshard 走,再 del-node
redis-cli -a pwd --cluster del-node 127.0.0.1:7000 <要删除的node-id>

六、生产避坑:这些事提前知道

现象应对
跨槽多 key 操作MSET/事务/ Lua 跨槽报 CROSSSLOT 错误相关 key 用 {hashtag} 强制同槽,如 user:{1001}:name
槽未全分配集群状态 fail,写入被拒CLUSTER INFOcluster_slots_assigned 是否为 16384
脑裂丢写主从网络抖动,从被提主后原主恢复min-replicas-to-write 限制最少从节点数
客户端不智能频繁 MOVED 报错、连接被打挂换 Cluster 客户端并定期刷新拓扑
大 key 迁移卡顿reshard 时单 key 过大拖慢整槽提前拆分大 key,避免单 value 上 GB

最容易被忽视的是大 key:集群迁移以 key 为最小单位,一个 2GB 的 hash 会让它所在的槽在 reshard 时长时间锁住,期间该槽读写都被阻塞。这和MySQL 深度调优里”避免单表过大”是同一种治理直觉——任何分片系统都怕”不均匀”。

七、和主从复制、哨兵的区别

三种方案常被混淆,职责其实很清晰:MySQL 主从复制那种”一主多从”是读扩展+备份,写仍单点;哨兵(Sentinel)在主从之上加了”自动选主”,解决高可用但不解决容量;集群则同时解决”容量横向扩展”和”高可用”。选型时先问自己:瓶颈是读、是可用性、还是单机容量?答案决定你该上哪一个。多数高并发缓存场景,直接上 Redis 集群最省心。

小结

Redis 集群用”16384 个哈希槽 + 三主三从”把横向扩展和高可用一次性解决:数据按 key 的 CRC16 落到槽、槽再分配到主节点,主挂了从节点自动顶上。搭建用 redis-cli --cluster create --cluster-replicas 1 一条命令成型,客户端务必用 Cluster 版处理 MOVED/ASK 重定向,扩容缩容靠 reshard 搬槽而非停服。真正要提前治理的是大 key 和跨槽多 key——它们才是集群平稳运行的隐形杀手。把它和Redis 缓存基础分库分表放在一起看,你对”数据怎么分、怎么高可用”的认知就完整了。

上一篇 tmux 终端复用实战:远程开发与会话保活
下一篇 模型蒸馏实战:知识蒸馏压缩大模型落地