Redis Cluster 集群部署与运维实战

单机 Redis 有两道天花板:一是内存容量,数据量超过单机内存就装不下;二是可用性,主进程挂掉只能等哨兵切换或人工拉起。当业务数据量上到几十 GB、又要求节点故障自动恢复时,官方的 Redis Cluster 是最直接的方案:它把数据按槽分片到多个主节点,每个主节点再挂从节点复制,兼顾水平扩展和高可用。

本文以 Redis 7.x 为准,用三主三从共六个节点,完整演示从部署、建群到扩容、缩容、故障转移的日常运维动作,并汇总生产中最常见的三类故障。示例使用两台主机各跑三个实例(生产建议六台或至少三台分散部署),IP 用 192.0.2.x 占位。

前置条件

  • 两台 Linux 主机 redis-01(192.0.2.11)、redis-02(192.0.2.12),已安装 Redis 7.x,版本保持一致。
  • 每台主机规划三个实例,端口 6379、6380、6381,数据目录分别为 /data/redis/6379、/data/redis/6380、/data/redis/6381。
  • 时间同步正常(chrony 或 ntpd),节点间时钟偏差会干扰故障判定。
  • 防火墙放行两类端口:业务端口 6379–6381,以及集群总线端口 = 业务端口 + 10000,即 16379–16381。总线端口供节点间交换状态与故障探测,只允许集群节点互访,不要对业务网段开放。
  • 所有实例统一设置密码(requirepass 与 masterauth 一致),建群和运维命令都要带 -a 认证。

一、节点配置要点

六个实例的 redis.conf 只有 port、dir、dbfilename 等路径类参数不同,集群相关参数完全一致。以 redis-01 的 6379 实例为例:

port 6379
bind 0.0.0.0
protected-mode no
daemonize yes
dir /data/redis/6379
dbfilename dump-6379.rdb
appendonly yes
requirepass YourPass
masterauth YourPass
cluster-enabled yes
cluster-config-file nodes-6379.conf
cluster-node-timeout 15000
cluster-require-full-coverage no

四个关键参数要理解透:

  • cluster-enabled yes:开启集群模式,节点不再接受普通单机复制配置。
  • cluster-config-file:集群节点配置文件,记录节点 ID、槽分配与投票状态,由 Redis 自动维护,人不要手动编辑;每实例必须各用一份,删掉它节点会以全新身份重新加入。
  • cluster-node-timeout 15000:节点超时毫秒数。超过该时间联系不上某节点,集群才认为它疑似下线(PFAIL),过半主节点确认后标记 FAIL 并触发转移。太小容易误判,太大故障恢复慢,生产常用 5000–15000。
  • cluster-require-full-coverage no:槽未全覆盖时仍允许读写已覆盖的槽,避免个别槽异常拖垮整个集群;对可用性敏感的业务建议设为 no。

六个实例逐台启动,确认每个进程都在监听且能 PING 通:

for p in 6379 6380 6381; do redis-server /etc/redis/$p.conf; done
redis-cli -p 6379 -a YourPass ping

二、创建集群

六个节点全部空数据启动后,一条命令建群。--cluster-replicas 1 表示每个主节点配一个从节点,redis-cli 会自动把六个节点分成三主三从并均衡分配槽:

redis-cli -a YourPass --cluster create \
  192.0.2.11:6379 192.0.2.11:6380 192.0.2.11:6381 \
  192.0.2.12:6379 192.0.2.12:6380 192.0.2.12:6381 \
  --cluster-replicas 1

命令会打印规划结果并等待确认,输入 yes 后执行。可以观察规划是否符合预期:16384 个槽被均分给三个主节点(0–5460、5461–10922、10923–16383),每个从节点对应一个主节点,且主从尽量不在同一台主机上。若从节点分布不理想,先不要确认,调整节点顺序重新执行。

三、健康检查与槽

集群状态

任意节点执行 CLUSTER INFO,重点看三个字段:cluster_state 为 ok、cluster_slots_assigned 为 16384、cluster_known_nodes 为 6。再用 CLUSTER NODES 查看全部节点的角色、槽范围与连接状态,第一列是每个节点的唯一 ID,迁移和删节点时要用:

redis-cli -c -p 6379 -a YourPass cluster info
redis-cli -p 6379 -a YourPass cluster nodes
redis-cli -a YourPass --cluster check 192.0.2.11:6379

--cluster check 会系统性地报告每个主节点的槽分布、副本情况和一致性问题,是巡检首选。写入验证时记得 redis-cli 加 -c 参数走集群模式,否则普通客户端连到不含目标键槽的节点会收到 MOVED 重定向报错。用 CLUSTER KEYSLOT 可以查看某个键落在哪个槽:

redis-cli -c -p 6379 -a YourPass set user:1001 "alice"
redis-cli -p 6379 -a YourPass cluster keyslot user:1001

槽与哈希标签

Cluster 把键空间切成 16384 个槽,键的槽号 = CRC16(键名) 对 16384 取模,槽再分配给各主节点。16384 是写死在协议里的常量:足够三主到上千节点的分片粒度,又能让节点间的心跳包体积可控。

哈希标签(hash tag)解决的是多键同槽问题:键名中出现一对大括号 {} 且括号内非空时,只对括号内的部分算哈希。例如 user:{1001}:profile 与 user:{1001}:orders 都只按「1001」计算,必然落在同一个槽,从而可以放在一条 MSET、事务或 Lua 脚本里。业务设计键名时,应把需要原子操作的一组键都打上同一标签;但也别把全站流量都打成一个标签,否则数据和热点全压在一个节点上,分片就白做了。

四、扩容:加节点与槽迁移

容量不足时,先备好一个全新空实例(如 redis-02 的 6382),以集群模式启动,然后加入集群。空节点先以从节点身份挂到某个主节点下:

redis-cli -a YourPass --cluster add-node 192.0.2.12:6382 192.0.2.11:6379 --cluster-slave

新节点进来时不持有任何槽,只是多一份复制。要让它变成主节点并分担数据,必须做槽迁移(reshard):把一部分槽连同其中的键从老主节点搬到新节点。reshard 是交互式的,依次询问迁移多少个槽(示例 1000)、接收方节点 ID(填新节点)、是单个源节点还是 all(示例选一个源节点),最后确认计划并输入 yes 执行:

redis-cli -a YourPass --cluster reshard 192.0.2.11:6379 \
  --cluster-from <源主节点ID> --cluster-to <新节点ID> \
  --cluster-slots 1000 --cluster-yes

迁移以槽为单位逐个搬键,大槽耗时明显,务必避开业务高峰;迁移中该槽的键可能短暂返回 ASK 重定向,支持集群协议的客户端会自动跟随,业务无感。迁完再把新节点的副本补齐:另起一个空实例,用 add-node --cluster-slave --cluster-master-id 指定新主节点的 ID 挂为它的从节点。

五、故障转移验证

自动转移

验证方法是真实拔掉一个主节点:在其所在主机执行 redis-cli -p 6380 shutdown nosave(验证环境),或直接 kill 进程。观察其余节点日志与 CLUSTER NODES 输出:cluster-node-timeout 设为 15000 时,约 15 秒后该节点被标 PFAIL,过半主节点确认后变为 FAIL,它的从节点随即被选为新主,接管原槽范围。整个过程通常在超时时间后数秒内完成。用 --cluster check 确认 cluster_state 回到 ok、每个槽都有主节点。

手动转移

计划内维护(升级、换机)应使用优雅的手动转移:在目标主节点的从节点上执行 CLUSTER FAILOVER,从节点与主节点完成一次数据同步后接管,旧主降为从节点,全程不断写:

redis-cli -p 6381 -a YourPass cluster failover

可选参数:FORCE 跳过与主节点的握手同步(主节点已失联但从节点还活着时用),TAKEOVER 连投票环节都跳过,属非常规手段,仅在集群已无法形成多数派时慎用。日常巡检也可用手动转移轮动主节点,提前演练切换路径。

六、缩容:删除节点

删节点分两种情况。从节点没有槽,直接 del-node 即可。主节点必须先把它的槽全部迁走:对该节点做 reshard,--cluster-slots 填它持有的槽总数、--cluster-to 指到其他主节点,迁空后它才允许被移除,强删持有槽的节点会被命令拒绝:

redis-cli -a YourPass --cluster del-node 192.0.2.11:6379 <节点ID>

被删节点要先停进程再删记录的说法并不成立:del-node 要求目标节点在线响应,顺序是先迁槽、再 del-node、最后停进程下线。全部完成后用 CLUSTER FORGET 在各节点清除残留的已删节点记录(del-node 已自动处理,FORGET 只作兜底),最后跑一遍 --cluster check 收尾确认。

常见问题

cluster_state:fail,提示槽未全覆盖

CLUSTER INFO 显示 cluster_state:fail 且 cluster_slots_ok 小于 16384,说明有槽没有主节点,常见于某主节点宕机且没有可用从节点接替。先定位缺失槽在哪个节点名下,若原节点能拉起则直接启动恢复;拉不起来则在其从节点执行 CLUSTER FAILOVER FORCE 强制接管;实在无副本时,可用 --cluster fix 尝试修复,或对丢失槽执行 CLUSTER SETSLOT … NODE 手工指派,并接受该部分数据丢失的现实。预防靠两点:每个主节点至少一个从节点、主从跨主机部署、不要关掉自动转移。

节点握手失败,日志刷屏 Unable to connect

节点能 PING 通业务端口、集群却始终组不起来,十有八九是总线端口不通:确认防火墙与安全组放行了端口 +10000(如 16379),并在源主机上用 nc -vz 192.0.2.12 16379 逐一验证。另一类原因是节点对外宣告的地址不对(NAT、云主机内外网映射),节点默认宣告连接来源看到的地址,双网卡环境需用 cluster-announce-ip、cluster-announce-port、cluster-announce-bus-port 显式写明业务侧可达的 IP 与端口。

多键命令报 CROSSSLOT

MSET、事务、Lua 脚本涉及的键不在同一个槽时,集群直接拒绝并返回 CROSSSLOT Keys in request don't hash to the same slot。对策是把这组键改为带相同哈希标签的键名(如 cart:{u1001}:a 与 cart:{u1001}:b);实在无法同槽的,就把多键操作拆成单键命令由客户端并发执行。另外注意单机习惯的迁移期命令同样受限:SELECT 在集群里只能用 0 号库,流水线与事务跨槽也会失败,接入集群前应让应用先做一轮键名与命令审计。

小结

Redis Cluster 运维的核心概念就两个:槽决定数据放哪儿,总线决定节点能否互相说话。部署时盯紧 cluster-config-file 的独立性、cluster-node-timeout 的合理取值和总线端口放行;日常用 CLUSTER INFO、CLUSTER NODES 与 --cluster check 做巡检;扩缩容通过 add-node、reshard、del-node 完成,全程不停机;故障切换优先信任自动转移,计划维护用 CLUSTER FAILOVER 优雅接管。把这几件事做成检查表,集群就能长期稳定运行。

发表回复

后才能评论