不少刚接触 Redis 的同学都有过这个疑问:我已经做了主从复制,写操作走主节点,读操作走从节点,数据也有了多副本备份,为什么还要再搞一套 Redis 集群?甚至有些项目已经上了主从方案,却仍然在高并发写入、大容量存储场景下频频出问题。
这篇文章不打算只讲概念,而是结合主从复制和 Redis Cluster 的底层机制,把两者解决的不同问题拆开来看。文章会覆盖主从复制的核心能力边界、引入集群的真实原因、Cluster 的分片路由原理、搭建步骤以及常见坑点。适合已经会基本 Redis 操作、但还没系统搞懂到底该用主从还是集群的开发者。
1. 主从复制:解决了什么问题,没解决什么问题
1.1 主从复制解决了什么
主从复制是 Redis 高可用体系里的第一层能力。一个主节点可以挂多个从节点,从节点实时同步主节点的数据。它的核心价值有两个:一是数据冗余,主节点挂了,从节点还有完整数据;二是读写分离,把读流量分摊到从节点,减轻主节点的读压力。
主从复制的基础流程大致是下面这样的:
- 从节点启动后向主节点发送
PSYNC命令请求同步。 - 主节点收到请求后执行
BGSAVE生成 RDB 快照,并把同步期间的新写命令记录到复制缓冲区。 - RDB 快照发送给从节点,从节点加载快照。
- 增量命令通过命令传播阶段持续同步到从节点。
这套机制保证从节点最终能拿到主节点的全量数据。在实际业务里,最常见的用法是:主节点负责读写,一台或多台从节点负责处理大量读请求,分担主库压力。
1.2 主从复制解决不了的问题
主从复制虽然解决了读扩展和数据备份问题,但它天生有两个瓶颈:
第一个瓶颈是写扩展能力为零。无论挂多少个从节点,所有写操作仍然只能打到主节点上。因为从节点的数据完全依赖于主节点的复制流,从节点自身不接收写请求。这就意味着当你业务写入量上升到单机无法承受的水平时,主从复制是救不了的。
第二个瓶颈是某块大内存数据集中在单机上。假设某个业务集合数据有 80GB,Redis 规定的maxmemory不能无限调大,因为单机物理内存上限是现实约束。即使你不在乎成本上了大内存机器,RDB 快照的BGSAVE时间会越来越长,主进程 fork 子进程时的内存开销也会增大,故障恢复的时间更久,这就是所谓的“大 Key 和单机容量困境”。
另外还要注意,主从复制本身不提供自动故障转移能力。传统主从方案在主节点宕机后,需要人工干预把从节点提升为主节点,或借助哨兵机制实现自动切换。关于哨兵后面会再展开,这里先记住一个结论:主从复制是一个数据层高可用方案,不是一个完整的分布式扩展方案。
2. Redis 集群:一套解决写扩展和容量扩展的架构
2.1 集群是什么
Redis 集群(Redis Cluster)从 Redis 3.0 开始正式提供,它把数据分散存储在多台节点上,从架构层面动态扩展读写能力和存储容量。集群中的每个节点都保存一部分数据,节点之间通过 Gossip 协议通信,维护整个集群的元数据。
和主从复制的本质差异在于:主从复制是同一份数据多个副本,写压力集中在主节点;Redis 集群是多份数据均匀分布在多个节点上,每台节点都有自己的主节点身份,都可以承担写压力。
用一句话概括就是:主从复制是对数据做副本冗余,集群是对数据做分片存储。
2.2 集群的典型架构
一个最简集群架构至少包含 3 个主节点,每个主节点可以挂一个或多个从节点。架构中每个分片负责一部分哈希槽,数据写入时根据 key 计算哈希槽,确定该 key 存储在哪个节点上。
这里有一个很重要的点:Redis Cluster 虽然叫集群,但它本身整合了“分片 + 副本 + 故障转移”三层能力。某分片的主节点挂了,该分片的从节点会自动提升为新主节点,保证整个集群继续对外服务。这一点和“主从复制 + 哨兵”模式有点相似,但 Sentinel 机制在 Cluster 模式下被去掉了,故障转移由集群内部完成,外部客户端无感知。
下面从数据分布算法、请求路由、故障转移三个阶段来看 Cluster 的核心原理。
3. 为什么主从复制不够,非要引入集群
3.1 从写能力瓶颈说起
假设项目日增数据量 20GB,高峰期写 QPS 达到 12 万。单台 Redis 主节点最多能支撑 8 万到 10 万 QPS 写入,这已经进入硬件极限。此时主从复制架构下不论怎么加从节点,主节点的写压力只会越来越严重,单点写入会成为系统的天花板。
集群模式下,三主节点的架构能把写请求分散到 3 台机器上,每台机器只需要扛 4 万 QPS,系统整体压力明显下降。如果后续写入量继续上涨,可以继续增加主节点,水平扩展写能力。
更直观一点理解:
- 主从复制架构的扩展边界:垂直扩展为主,把单机内存加大、CPU 升级。
- 集群架构的扩展边界:水平扩展为主,增加节点数量即可提升整体能力。
3.2 从单机容量限制说起
假设业务缓存数据量是 120GB,单台物理机内存 128GB,理论上可以勉强容纳,但 Redis 还要留出系统开销和内存碎片空间,这种情况在运行期很容易触达内存上限触发淘汰策略。更关键的是 RDB 快照备份耗时可能达到数分钟,故障恢复期间的大量请求会直接穿透到 DB。
集群将 120GB 数据按哈希槽范围拆到多个节点上,每个节点只负责一部分数据。比如三主节点架构下每个节点大约存 40GB,这样单节点内存压力小,快照和恢复速度更快,数据容量可随节点数量线性扩展。
3.3 从故障影响范围说起
在主从复制模式下,主节点故障如果没有哨兵,Redis 直接不可写,直到人工切换主备节点。即使配置了哨兵,故障切换期间主节点上的所有 key 都可能短时不可用,服务抖动不可避免,但整个实例的数据都存在一台机器上,单点故障影响面是 100%。
在集群模式下,某个分片主节点故障只影响该分片内的数据,大约是总数据量的 N 分之一,其他分片继续服务。从故障爆炸半径这个角度看,集群的隔离性优于单主从。
3.4 对比表格
| 维度 | 主从复制 | Redis 集群 |
|---|---|---|
| 数据冗余 | 每个从节点都是全量副本 | 每个分片有主从节点,数据分片冗余 |
| 写扩展能力 | 不支持,写固定在主节点 | 支持,主节点分片后共同抗写 |
| 容量扩展 | 受单机内存限制 | 可横向扩展,增加节点即扩容 |
| 高可用 | 需要额外配置哨兵或人工处理 | 原生支持自动故障转移 |
| 数据分布 | 所有节点数据相同 | 数据按哈希槽分布在各节点 |
| 多 key 操作 | 单实例内可直接执行 | 跨 slot 受限,需使用 Hash Tag |
| 运维复杂度 | 简单 | 相对复杂,需考虑槽位和节点管理 |
| 适用场景 | 读多写少、数据量可控 | 写多、数据量大、持续增长 |
4. 深入理解 Redis Cluster 数据分片原理
4.1 哈希槽:数据分布的基石
Redis Cluster 一共有 16384 个哈希槽,每个 key 根据以下公式确定自己属于哪个槽:
HASH_SLOT = CRC16(key) % 16384CRC16 是 Redis 内置的一种哈希算法,会把任意字符串映射为一个 16 位整数值,再对 16384 取模,得到 0 到 16383 之间的槽号。
集群在创建时会把这些槽分配给具体的主节点。例如三主节点集群,常见分配方式:
- 节点 A 负责槽 0 - 5460
- 节点 B 负责槽 5461 - 10922
- 节点 C 负责槽 10923 - 16383
客户端写入product:1001时,同样计算哈希槽,然后定位到对应节点执行操作。如果客户端连的是任意节点而该 key 不属于这个节点,节点会返回MOVED重定向指令,告诉客户端应该去哪个节点访问。
4.2 为什么不直接把 key 映射到节点
把 key 直接按比例映射到节点,扩展性很差。比如原有 3 个节点,每个 key 按hash % 3分布,当节点增加到 4 个时,绝大多数 key 的映射位置都会改变,缓存全部失效,大量请求穿透到后端数据库。
哈希槽的方案把映射关系分为两层:第一层是 key 到槽的固定映射,永远不会变;第二层是槽到节点的映射,可以灵活调整。扩容时只需要把一部分槽从旧节点迁移到新节点,key 到槽的计算不变,只影响迁移的那部分数据,影响范围可控。
4.3 MOVED 重定向
来看一个具体例子。假设集群有三个主节点 A、B、C,客户端连接了节点 A,写入一个site:blog的 key:
127.0.0.1:7000> SET site:blog csdn.net (error) MOVED 3000 192.168.1.102:7001这说明site:blog计算出的哈希槽是 3000,槽 3000 分布在节点 B 上。节点 A 不认识这个 key,于是告诉客户端去192.168.1.102:7001执行。
主流客户端比如 Jedis、Lettuce、RedisTemplate 都已经内置了槽路由缓存。客户端第一次收到 MOVED 后会更新本地槽映射表,后续请求直接发到正确的节点,减少重定向带来的性能损耗。
4.4 ASK 重定向与槽迁移
扩容或缩容时,Redis Cluster 需要把一部分槽和槽内的数据迁移到新节点。迁移过程中如果客户端访问的 key 正好在迁移范围内,源节点会返回ASK重定向。
ASK 和 MOVED 的区别是:MOVED 表示槽的归属已经永久改变,客户端要更新本地缓存;ASK 表示槽正在迁移,数据可能还在源节点,客户端需要先发ASKING命令让目标节点临时接受这次访问。
迁移期间,源节点会持续把槽中数据同步到目标节点,同时仍然接收读写请求。直到所有 key 迁移完毕,再把槽的归属通知到整个集群。
5. Redis 主从复制详细配置与实操演示
先看主从复制的完整搭建。这里用本地多实例的方式演示,生产环境可以替换为真实 IP 和多台服务器。
5.1 准备多个 Redis 实例
我本机环境以 Redis 6.x 常见版本为例,在/usr/local/redis目录下创建conf目录,分别放置主节点和从节点的配置文件。
主节点配置文件/usr/local/redis/conf/redis-6379.conf:
port 6379 daemonize yes pidfile /var/run/redis-6379.pid logfile /var/log/redis/redis-6379.log dir /usr/local/redis/data/6379 requirepass 123456 appendonly yes从节点配置文件/usr/local/redis/conf/redis-6380.conf:
port 6380 daemonize yes pidfile /var/run/redis-6380.pid logfile /var/log/redis/redis-6380.log dir /usr/local/redis/data/6380 requirepass 123456 masterauth 123456 replicaof 127.0.0.1 6379 appendonly yes从节点关键配置是replicaof,它指定了主节点的 IP 和端口。6.0 之前的版本这个配置项叫slaveof,6.0 之后官方已经统一更名为replicaof。
启动两个实例:
redis-server /usr/local/redis/conf/redis-6379.conf redis-server /usr/local/redis/conf/redis-6380.conf查看主从关系:
redis-cli -p 6379 -a 123456 info replication预期结果中role:master,connected_slaves:1。再查看从节点:
redis-cli -p 6380 -a 123456 info replication从节点输出中role:slave,master_host指向主节点。
5.2 验证主从数据同步
向主节点写入一条数据:
redis-cli -p 6379 -a 123456 SET article:1001 redis-master从节点读取验证:
redis-cli -p 6380 -a 123456 GET article:1001可以看到从节点查到了相同的数据,主从复制生效。此时如果直接向从节点写入:
redis-cli -p 6380 -a 123456 SET article:1002 error-test在默认配置下从节点会报错:
(error) READONLY You can't write against a read only replica.这就是主从复制的本质约束:从节点只读,所有写请求都必须发送到主节点。
5.3 哨兵在主从复制中的角色
前面提到主从复制本身不处理主节点故障,于是我们引入哨兵。哨兵的核心作用是监控主从节点状态,在主节点宕机时自动执行故障转移,选出一个从节点提升为新主节点。
最少哨兵数量推荐 3 个,避免哨兵自身单点故障导致误判。一个最简哨兵配置文件sentinel.conf:
port 26379 daemonize yes sentinel monitor mymaster 127.0.0.1 6379 2 sentinel auth-pass mymaster 123456配置中2表示至少两个哨兵同意才能判定主节点客观下线并触发故障转移。
启动哨兵:
redis-sentinel /usr/local/redis/conf/sentinel.conf此时主从复制加哨兵已经可以做到:
- 主节点正常时,读写分离正常工作。
- 主节点故障时,哨兵自动选择一个从节点提升为主节点。
- 客户端连接主节点地址变化时,通过哨兵接口获取新的主节点地址。
这个架构看起来很完善,但注意它依然没有解决写扩展和容量扩展的问题。即使有 10 个从节点,写入还是只能落在唯一的主节点上。
6. Redis 集群搭建完整实战
下面用本地 6 个 Redis 实例搭建一个三主三从的集群,这是在开发环境验证集群特性的常用方式。
6.1 创建节点配置文件
为了快速搭建,在/usr/local/redis/conf下创建 6 个配置文件,端口分别为 7000 到 7005。以redis-7000.conf为例:
port 7000 daemonize yes cluster-enabled yes cluster-config-file nodes-7000.conf cluster-node-timeout 5000 appendonly yes dir /usr/local/redis/data/7000 requirepass 123456 masterauth 123456其他端口的配置文件只需替换端口号和目录名。核心配置是cluster-enabled yes,它决定这个 Redis 实例以集群节点模式运行。cluster-config-file是集群元数据文件,由 Redis 自动维护,不需要手工编辑。
启动全部节点:
redis-server /usr/local/redis/conf/redis-7000.conf redis-server /usr/local/redis/conf/redis-7001.conf redis-server /usr/local/redis/conf/redis-7002.conf redis-server /usr/local/redis/conf/redis-7003.conf redis-server /usr/local/redis/conf/redis-7004.conf redis-server /usr/local/redis/conf/redis-7005.conf6.2 创建集群
执行以下命令把 6 个节点组成集群:
redis-cli --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 -a 123456参数说明:
- 前三个节点作为主节点。
--cluster-replicas 1表示为每个主节点分配一个从节点。- 后三个节点自动成为前三个主节点的从节点。
命令执行后 Redis 会打印槽分配计划并询问是否确认,输入yes继续。
创建完成后可以查看集群状态:
redis-cli --cluster check 127.0.0.1:7000 -a 123456预期的状态是三个主节点分别拥有不同范围的哈希槽,每个主节点都有一个从节点在线。
6.3 集群模式下写数据验证
使用-c参数连接集群模式下的客户端:
redis-cli -c -p 7000 -a 123456执行写入操作:
127.0.0.1:7000> SET article:1001 redis-cluster-test OK 127.0.0.1:7000> GET article:1001 "redis-cluster-test"加-c后客户端会自动处理 MOVED 重定向,不需要手动切换节点。
6.4 验证集群故障转移
手动模拟一个分片主节点宕机,看看集群如何自愈。比如杀掉 7001 端口的节点进程:
redis-cli -p 7001 -a 123456 shutdown nosave等待一段时间后查看集群状态:
redis-cli --cluster check 127.0.0.1:7000 -a 123456可以看到 7001 对应的从节点已经自动提升为主节点,整个集群仍然处于在线状态。这就是集群原生高可用能力的体现。
7. 主从复制和集群的实际选型建议
7.1 数据量在单机范围时,优先主从复制
如果数据总量不超过单机内存 80%,读多写少,例如配置中心、活动页缓存、验证码存储,主从复制加哨兵已经足够。这种场景下用集群反而增加了运维成本,因为需要额外管理节点间槽位迁移、客户端路由和跨槽操作限制。
7.2 数据量持续增长时,选择集群
如果业务处于快速增长期,按中位数估算半年后数据量会明显超过单机承载范围,直接上集群更合适。集群虽然初期运维成本高一些,但避免了后续从主从迁移到集群的数据搬迁工作,这个成本往往比新搭建集群高得多。
7.3 读写比例和写并发是重要判断依据
- 读写比例 10:1,总 QPS 以读为主,数据量可控,优先主从复制。
- 写 QPS 持续接近单实例上限,优先集群。
- 单分片热点明确,比如某个 key 的访问量占 90%,优先考虑缓存架构优化,而不是单纯依赖集群。
7.4 两种架构可以结合吗
可以。生产环境常见的是基于集群架构,同时又对集群中的每个节点配置主从复制副本。因为集群的分片主节点同样需要高可用,一个分片主节点挂了,由该分片的从节点接管。也就是说,集群内部天然包含了一层主从复制的副本机制,两者不是互斥关系,而是层级配合关系。
8. 集群使用中的常见问题与排查思路
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
MOVED重定向频繁出现 | 客户端未启用集群模式 | 客户端连接参数开启 cluster 模式 |
(error) CLUSTERDOWN The cluster is down | 哈希槽没有完全覆盖 16384 个槽 | 检查槽分配,补全缺失槽位 |
| 跨 slot 多 key 操作报错 | 多个 key 的哈希槽不在同一节点 | 使用 Hash Tag,如{user1001}:name |
ASK重定向出现 | 槽迁移中 | 客户端支持 ASK 即可自动处理 |
集群创建失败提示ERR Slot | 节点之前已经初始化过集群 | 清空 nodes 配置文件和 AOF/RDB,重新搭建 |
| 主从切换后写入丢失 | 主节点故障时存在未复制完的写命令 | 根据业务容忍度开启wait保证同步 |
8.1 多 key 操作受限问题
集群模式下执行多 key 操作,如果 key 没有使用哈希标签,比如:
127.0.0.1:7000> mget user:1:name user:2:name (error) CROSSSLOT Keys in request don't hash to the same slotRedis 会拒绝执行,因为两个 key 可能分布在不同节点。解决方式是使用哈希标签,让相关 key 映射到同一个槽。
127.0.0.1:7000> mget {user}:1:name {user}:2:name哈希标签只花括号内的部分参与哈希计算,因此{user}相同的 key 会分布到同一个槽,可以在同一个节点上执行多 key 操作。
8.2 槽迁移期间访问变慢
扩容时需要把旧节点的部分槽迁移到新节点。迁移过程涉及大量MIGRATE命令,会占用源节点 CPU 和网络 IO。建议在业务低峰期执行槽迁移,分批次迁移,避免单次大数据量迁移拖垮节点。
9. 最佳实践与工程经验
9.1 集群规模规划
生产环境建议每个分片主节点挂载一个从节点,既保证高可用,又避免从节点过多造成复制成本和资源浪费。初期以 3 主 3 从起步,后续随着数据量扩容按每个主节点承载预算内存新增节点。
规划时预先留出扩容位,比如设定每个主节点内存上限 8GB,物理机总内存 32GB,那这台机器理论上可以承载 3 个主节点实例,但最好只跑两个主节点并预留一个实例容量的系统余量。
9.2 避免大 Key 写入集群
集群虽然把数据分散到了多个节点,但单个 key 的 value 体积仍然会成为一个节点的风险点。大 Key 会造成节点内存不均、阻塞网络、迁移卡顿等问题。写入前评估单个 key 的 value 大小,建议单 value 控制在 1MB 以下。遇到大集合时拆分 key,按业务维度设计多个 key。
9.3 合理使用 Hash Tag
Hash Tag 可以把多个相关 key 强制放到同一个槽,解决多 key 操作问题。但滥用 Hash Tag 会导致大量 key 堆积在同一个槽,引发数据倾斜。只在明确需要多 key 事务操作的场景使用,比如用户维度下的多个属性字段。
9.4 客户端配置方面
生产环境使用支持集群模式的高版本客户端,推荐 Lettuce 或 Jedis 的 Cluster 版本。客户端需要配置连接池,设置合理的超时时间,防止节点切换期间请求阻塞过长时间。遇到节点切换时,客户端应当自动更新槽路由信息,这要求连接参数中指定初始节点列表包含全部主节点。
9.5 监控与报警
无论使用主从复制还是集群,都必须监控以下指标:
- 内存使用率。
- 命中率。
- 主从复制延迟。
- 节点在线状态。
- 槽位覆盖状态。
- key 数量和各节点内存分布均衡度。
集群模式下特别关注节点间内存是否倾斜,如果某个节点内存明显高于其他节点,大概率是热点 key 或 Hash Tag 使用过度。
9.6 数据备份与恢复
集群模式下备份方式和单实例不同。不能简单在任意节点生成 RDB,因为每台节点都只保存部分数据。需要逐节点备份 RDB 文件,恢复时也要按节点逐个恢复。有条件的情况下,建议开启 AOF 并定期把 RDB 文件归档到独立存储服务器。涉及删除操作或崩溃恢复前,尽量先在测试集群演练一遍完整流程。
10. 版本演进与安全边界
Redis 集群相关能力在不同版本之间有差异。3.0 是集群功能正式发布的版本,后面多个版本逐步完善了节点迁移、集群管理命令、客户端协议等能力。生产环境部署建议选择官方维护周期内的稳定版本。
需要特别注意的是,Redis 在配置密码保护时,集群节点间的认证也要同步配置。每台节点的requirepass和masterauth必须保持一致,否则从节点连接主节点时会认证失败,集群无法正常建立。
对外提供服务时,不要让 Redis 端口直接暴露公网。集群节点端口,包括客户端端口和集群内部通信端口,都应该通过防火墙或安全组限制访问来源,只允许应用服务器网段访问。
11. 写在最后
回到开头的那个问题:有了主从复制,为什么还要 Redis 集群?主从复制的本职工作是数据和读流量的水平扩展,而写流量和高容量存储注定需要把数据拆散到多台节点上。Redis Cluster 是在主从复制之上的分布式解决方案,它用哈希槽完成了数据分片,用主从节点组实现了分片高可用,让 Redis 从单机缓存进化成真正能横向扩容的存储系统。
实际落地时,没有绝对的好坏架构,只有匹配业务场景的方案。小数据量读多写少,主从复制加哨兵完全够用;业务高速增长、写并发高、容量未来会超出单机范围,就尽早规划集群。希望这篇文章能帮你做出更合理的技术选型,避免在架构升级路上重复踩坑。