☰
Redis分片集群实战:从搭建到运维的完整指南
2026/10/26 11:53:05 网站建设 项目流程

1. 分片集群到底解决了什么问题

单机 Redis 撑不住的时候,很多人第一反应是加内存、换更好的机器,也就是常说的“向上扩容”。这条路在数据量不大时确实省事,但一旦缓存数据涨到几十 GB、写入 QPS 冲到十万级,单机就会撞上三重天花板:内存容量受物理硬件限制、单线程处理能力有上限、主从架构下全量数据仍要放在一台主节点上。主从加哨兵能解决高可用,却解决不了“一份数据装不下”的问题,因为从节点只是主节点的完整副本,数据总量并没有被拆开。

分片集群的思路完全不同,它把整个键空间按规则切成若干份,每份交给不同的主节点负责,多个主节点各自承担一部分读写,横向叠加出容量和吞吐。你可以把它理解成一家快递站爆仓后不是把仓库盖得更高,而是开了多个分站,每个分站只负责一片区域的包裹,客户寄件时先按地址找到对应分站。Redis Cluster 就是官方给出的这套分站方案,它同时内置了数据分片、节点间通信和故障转移,不需要额外引入代理层。

这套机制适合谁?如果你正在做缓存层容量规划,发现单实例内存已经逼近 20GB 以上还在涨;或者业务写入压力大,单主节点 CPU 长期打满;又或者你希望缓存层具备自动故障转移能力,同时不想维护哨兵这套额外组件,那分片集群就是绕不开的一课。反过来,如果数据量只有几个 GB、QPS 几千,硬上集群只会增加运维复杂度,得不偿失。我见过不少团队在数据量还很小的时候就搭集群,结果节点间心跳、槽位迁移带来的问题比收益还多,这一点后面会细说。

需要先明确一个概念:Redis Cluster 的分片单位是哈希槽,总共 16384 个槽,每个键通过 CRC16 计算后对 16384 取模,落到某个槽上,槽再分配给具体节点。为什么是 16384 而不是更大的数?这和节点间心跳包的大小有关,槽位信息要随心跳广播,16384 个槽用位图表示只占 2KB,节点数量在千级以内时通信开销可控,这是官方在容量和开销之间做的权衡。理解了这个数字的来源,后面配置时看到16384就不会觉得神秘。

2. 搭建前的规划与关键参数取舍

2.1 节点数量与主从配比怎么定

官方推荐的最小生产规模是3 主 3 从,共 6 个节点。为什么主节点至少 3 个?因为集群的故障转移依赖节点间投票,主节点数量为奇数才能避免脑裂时的平票僵局,3 个主节点允许挂掉 1 个仍能正常选举。如果只搭 2 主,一旦一个主节点失联,剩下的节点无法形成多数派,集群会停止服务,这是很多人踩过的坑。

主从配比上,每个主节点配 1 个从节点是起步配置,能保证单主故障时数据不丢、服务不断。如果对读扩展有需求,可以给热点主节点配 2 个从节点,但要注意从节点只分担读,写仍然集中在主节点。我个人的经验是:先按 3 主 3 从搭起来跑通,再根据实际读压力决定是否加从节点,不要一上来就堆节点,节点越多心跳和槽位同步的负担越重。

端口规划上,每个 Redis 节点需要两个端口:一个客户端端口(如 7000),一个集群总线端口(客户端端口 + 10000,即 17000),后者用于节点间 gossip 通信。这两个端口都必须在防火墙放行,只开客户端端口是最常见的搭建失败原因之一,表现为节点能连上但集群状态一直是fail。

2.2 配置文件里必须改的那几项

集群模式的配置和单机差别不大,但有几个参数不改就一定跑不起来。下面是我实际使用的一套最小配置模板,以 7000 端口节点为例:

port 7000 cluster-enabled yes cluster-config-file nodes-7000.conf cluster-node-timeout 15000 appendonly yes dir /data/redis/7000 daemonize yes protected-mode no bind 0.0.0.0

逐项说明取舍逻辑。cluster-enabled yes是开关,不开就是普通实例。cluster-config-file是节点自己维护的集群状态文件,不要手动编辑,它由节点自动重写,手动改容易导致状态错乱。cluster-node-timeout 15000表示节点失联超过 15 秒才判定为故障,这个值太小会因网络抖动误判主从切换,太大则故障恢复慢,15 秒是多数场景的折中值,内网环境可以适当调小到 5000 到 10000。

appendonly yes建议开启,集群环境下主从切换频繁,AOF 比 RDB 的数据丢失窗口更小。protected-mode no和bind 0.0.0.0是为了让其他节点能连进来,但生产环境务必配合防火墙或安全组限制来源 IP,不要裸奔在公网。dir目录要提前创建好,权限给对,否则启动时报错还不好排查。

注意:6 个节点的cluster-config-file和dir必须各不相同,端口也要错开。复制配置时最容易漏改这两项,导致节点启动后互相覆盖状态文件。

2.3 硬件与网络的前置检查

分片集群对网络延迟比单机敏感得多,节点间心跳、槽位迁移、主从复制都走网络。内网环境建议节点间 RTT 控制在 1ms 以内,跨机房部署要慎重,网络抖动会直接触发误判切换。磁盘方面,AOF 开启后写入量不小,用普通 SSD 即可,不必上高端存储,瓶颈通常在网络和内存而不是磁盘 IO。

内存规划上,每个节点要预留一部分给复制缓冲区和集群元数据,实际可用内存大约是maxmemory的 70% 到 80%。如果 6 个节点部署在同一台物理机上做测试,记得把每个实例的maxmemory调小,否则容易触发系统 OOM。我测试环境就吃过这个亏,6 个实例各配 2GB,机器总共 8GB,跑一会儿就被系统杀进程,后来统一降到 512MB 才稳定。

3. 一步步把集群搭起来

3.1 启动六个节点并确认状态

先把 6 份配置准备好,分别对应 7000 到 7005 端口,目录和状态文件按端口区分。启动命令很朴素:

redis-server /data/redis/7000/redis.conf redis-server /data/redis/7001/redis.conf # 依次启动到 7005

启动后逐个用redis-cli -p 7000连上去执行cluster info,此时应该看到cluster_state:fail,这是正常的,因为还没分配槽位。再看cluster nodes,每个节点只认识自己,状态是myself,master。这一步的关键是确认每个节点都正常监听、日志里没有报错,尤其是端口占用和目录权限问题,早发现早处理。

如果某个节点起不来,先看日志里的dir路径是否存在、cluster-config-file是否可写。还有一种情况是之前搭过集群残留了状态文件,新配置和旧状态冲突,直接删掉旧的nodes-*.conf再启动即可,测试环境可以这么干,生产环境要谨慎。

3.2 用 cluster create 组建集群

节点都起来后,用一条命令完成组群和槽位分配:

redis-cli --cluster create \ 192.168.1.10:7000 192.168.1.10:7001 192.168.1.10:7002 \ 192.168.1.10:7003 192.168.1.10:7004 192.168.1.10:7005 \ --cluster-replicas 1

--cluster-replicas 1表示每个主节点配一个从节点,工具会自动把前 3 个当主、后 3 个当从,并把 16384 个槽平均分成 3 份,每份约 5461 个槽。执行过程中会提示你确认槽位分配方案,输入yes后开始。这里有个细节:主从的对应关系是工具自动决定的,如果你对节点角色有特定要求(比如希望从节点和主节点不在同一台机器),需要手动调整,或者用--cluster-replicas配合后续的cluster replicate命令重新指定。

组群完成后,任意节点执行cluster info应看到cluster_state:ok,cluster nodes能看到 6 个节点、3 主 3 从,每个主节点后面跟着它负责的槽位区间。到这一步集群就算搭好了,但别急着上业务,先做一轮验证。

3.3 验证分片与故障转移是否真的生效

验证分片最直接的办法是写入不同键,观察它们落到哪个节点。用redis-cli -c -p 7000进入集群模式(-c表示自动跟随重定向),然后:

set user:1001 "alice" set order:2001 "paid"

如果键不在当前节点负责的槽上,客户端会收到MOVED重定向,-c模式下会自动跳到正确节点执行。你可以用cluster keyslot user:1001算出键对应的槽号,再对照cluster nodes里的槽区间,确认落点符合预期。这一步能直观感受到“数据被拆开了”。

故障转移验证更关键。找到某个主节点的进程,直接kill掉,然后在从节点上观察cluster nodes,大约经过cluster-node-timeout的时间后,从节点会提升为主节点,槽位随之转移,集群状态恢复ok。这个过程我实测下来,15 秒超时配置下大约 20 秒左右完成切换,期间对应槽位的读写会短暂失败,客户端需要具备重试能力。验证完记得把原来的节点重新拉起,它会以从节点身份重新加入。

提示:故障转移测试一定要在业务低峰做,切换瞬间对应槽位的请求会报CLUSTERDOWN或连接错误,线上直接测可能引发告警。

4. 分片集群的原理拆解

4.1 哈希槽与键的路由逻辑

前面提到键通过 CRC16 取模落到 16384 个槽之一,这个设计的好处是槽位和节点的解耦。数据迁移时移动的是槽,而不是逐个键搬迁,一个槽对应一批键,迁移粒度更粗但效率更高。客户端拿到槽位映射表后,本地缓存一份,请求时先算槽再找节点,只有映射变化时才重新拉取,这就是所谓“智能客户端”。

MOVED和ASK两种重定向要分清。MOVED表示槽的归属已经永久变更,客户端应更新本地映射表;ASK表示槽正在迁移中,这个键暂时不在目标节点,客户端这次请求要去临时节点取,但不更新本地映射。很多客户端 bug 就出在把ASK当成MOVED处理,导致映射表被污染。自己写客户端或排查路由问题时,这两个语义必须吃透。

4.2 节点间通信与故障判定

集群总线走的是二进制协议,节点之间通过 gossip 互相交换状态:谁活着、谁负责哪些槽、谁是主谁是从。每个节点定期向随机几个节点发 ping,收到 pong 就更新对方的心跳时间。当某个节点在cluster-node-timeout内没有响应,其他节点会把它标记为pfail(主观下线),当超过半数主节点都认为它下线,就升级为fail(客观下线),触发从节点选举。

选举用的是 Raft 的简化版,从节点向其他主节点拉票,拿到多数票的从节点晋升为主。这里有个容易被忽略的点:从节点的数据新鲜度影响选举优先级,复制偏移量越大的从节点越容易被选中,避免选出一个数据落后的节点当主。所以主从复制的健康状况直接关系到故障转移后的数据完整性,平时要监控复制延迟。

4.3 槽位迁移的完整流程

扩容或缩容时,槽位需要在节点间搬迁。以把 7000 上的一个槽迁到 7003 为例,流程分几步:目标节点先执行cluster setslot <slot> importing <source-id>标记为导入中,源节点执行cluster setslot <slot> migrating <target-id>标记为迁出中,然后源节点用cluster getkeysinslot分批取出槽内的键,逐个migrate到目标节点,最后两边执行cluster setslot <slot> node <target-id>完成归属变更。

迁移过程中,源节点对已迁走的键返回ASK重定向,对还没迁的键正常处理,所以业务基本无感。但要注意migrate是同步阻塞的,大批量键迁移会占用源节点资源,建议在低峰期做,并且用--cluster-pipeline控制每次迁移的键数量。我一般把每批控制在 100 个键以内,避免单次迁移卡太久。

5. 常见问题与排查实录

5.1 集群状态一直 fail 怎么查

这是搭建阶段最高频的问题。排查顺序建议固定下来:先cluster info看cluster_state和cluster_slots_assigned,如果已分配槽数不是 16384,说明槽没分完;再看cluster nodes里有没有节点处于fail或handshake状态;最后检查集群总线端口(客户端端口 + 10000)是否互通。我遇到过最隐蔽的一次是安全组只放了客户端端口,节点间 gossip 不通,表现就是每个节点都只认识自己,cluster nodes里孤零零一个myself。

5.2 客户端报 MOVED 频繁怎么办

正常情况下客户端应该缓存槽映射,只在收到MOVED时更新。如果频繁收到MOVED,说明本地映射表过期了,常见原因是客户端实现没有正确处理重定向,或者集群正在做槽迁移。先确认集群是否在迁移(cluster nodes里看有没有migrating标记),如果没在迁移还频繁MOVED,就要检查客户端版本和配置,有些老版本客户端不支持集群模式,需要换用支持 cluster 的客户端或加代理层。

5.3 主从切换后数据不一致

切换后从节点晋升,如果原主节点还有未同步的写入,这部分数据就丢了。根因是主从复制是异步的,主节点写完就返回,不等从节点确认。缓解办法是开启min-replicas-to-write和min-replicas-max-lag,要求至少 N 个从节点在 M 秒内确认,否则主节点拒绝写入,用可用性换一致性。这两个参数要根据业务对数据丢失的容忍度来定,缓存场景通常可以放宽,因为缓存本身允许回源重建。

下面这张表是我整理的常见问题速查,按现象、可能原因、处理动作三列对照:

现象可能原因处理动作
cluster_state:fail槽未分配完或总线端口不通检查槽分配、放行端口+10000
节点只认识自己集群总线通信失败检查防火墙、bind 配置
频繁 MOVED客户端映射过期或槽迁移中升级客户端、确认迁移状态
切换后数据丢失异步复制未同步配置 min-replicas 参数
迁移卡住大键或网络慢分批迁移、拆分大键

5.4 大键对集群的隐性伤害

分片集群里大键的危害被放大了。一个几 MB 的 hash 或 list,在槽迁移时会作为一个整体搬迁,阻塞迁移流程;在故障转移时,大键的复制也会拖慢同步。更麻烦的是大键往往集中在少数槽上,导致节点间负载不均。我的处理习惯是上线前用redis-cli --bigkeys扫一遍,超过 10KB 的键就要评估是否拆分,比如把大 hash 按字段前缀拆成多个小 hash,用业务层做聚合。

6. 生产环境的运维心得

集群搭起来只是开始,长期稳定靠的是日常监控和容量管理。监控上我重点关注三类指标:节点内存使用率和maxmemory的比值、主从复制偏移量差值、集群状态和槽覆盖情况。内存逼近阈值前就要规划扩容,别等触发淘汰策略影响命中率。复制偏移量差值持续增大说明从节点跟不上,可能是网络或从节点负载问题,要尽早处理,否则故障转移时选出的新主数据落后太多。

扩容操作我建议用redis-cli --cluster add-node加新节点,再用--cluster reshard迁移槽,迁移时按槽区间分批做,每批迁完观察一轮集群状态和业务延迟,确认稳定再继续。缩容则反过来,先把待下线节点的槽全部迁走,确认它不再负责任何槽,再用--cluster del-node移除。整个过程最忌讳一次性大动作,分步走、每步验证,出问题也容易回退。

最后分享一个我踩过的坑:集群节点的cluster-config-file曾经因为磁盘满写入失败,节点重启后状态文件损坏,重新加入集群时槽信息错乱,花了不少时间才恢复。从那以后我把状态文件所在目录单独做了磁盘监控,并且定期备份nodes-*.conf,虽然它不该手动改,但备份一份在灾难恢复时能省很多事。集群这东西,搭建本身不难,难的是把边界情况和运维细节想周全,希望这些经验能帮你少走点弯路。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询