主从复制与Redis集群深度对比:从数据分片到水平扩展的架构演进

发布时间:2026/9/6 4:23:26
主从复制与Redis集群深度对比:从数据分片到水平扩展的架构演进 不少刚接触 Redis 的同学都有过这个疑问我已经做了主从复制写操作走主节点读操作走从节点数据也有了多副本备份为什么还要再搞一套 Redis 集群甚至有些项目已经上了主从方案却仍然在高并发写入、大容量存储场景下频频出问题。这篇文章不打算只讲概念而是结合主从复制和 Redis Cluster 的底层机制把两者解决的不同问题拆开来看。文章会覆盖主从复制的核心能力边界、引入集群的真实原因、Cluster 的分片路由原理、搭建步骤以及常见坑点。适合已经会基本 Redis 操作、但还没系统搞懂到底该用主从还是集群的开发者。1. 主从复制解决了什么问题没解决什么问题1.1 主从复制解决了什么主从复制是 Redis 高可用体系里的第一层能力。一个主节点可以挂多个从节点从节点实时同步主节点的数据。它的核心价值有两个一是数据冗余主节点挂了从节点还有完整数据二是读写分离把读流量分摊到从节点减轻主节点的读压力。主从复制的基础流程大致是下面这样的从节点启动后向主节点发送PSYNC命令请求同步。主节点收到请求后执行BGSAVE生成 RDB 快照并把同步期间的新写命令记录到复制缓冲区。RDB 快照发送给从节点从节点加载快照。增量命令通过命令传播阶段持续同步到从节点。这套机制保证从节点最终能拿到主节点的全量数据。在实际业务里最常见的用法是主节点负责读写一台或多台从节点负责处理大量读请求分担主库压力。1.2 主从复制解决不了的问题主从复制虽然解决了读扩展和数据备份问题但它天生有两个瓶颈第一个瓶颈是写扩展能力为零。无论挂多少个从节点所有写操作仍然只能打到主节点上。因为从节点的数据完全依赖于主节点的复制流从节点自身不接收写请求。这就意味着当你业务写入量上升到单机无法承受的水平时主从复制是救不了的。第二个瓶颈是某块大内存数据集中在单机上。假设某个业务集合数据有 80GBRedis 规定的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的 key127.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.confport 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.confport 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 之前的版本这个配置项叫slaveof6.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:masterconnected_slaves:1。再查看从节点redis-cli -p 6380 -a 123456 info replication从节点输出中role:slavemaster_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 cant write against a read only replica.这就是主从复制的本质约束从节点只读所有写请求都必须发送到主节点。5.3 哨兵在主从复制中的角色前面提到主从复制本身不处理主节点故障于是我们引入哨兵。哨兵的核心作用是监控主从节点状态在主节点宕机时自动执行故障转移选出一个从节点提升为新主节点。最少哨兵数量推荐 3 个避免哨兵自身单点故障导致误判。一个最简哨兵配置文件sentinel.confport 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}:nameASK重定向出现槽迁移中客户端支持 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 dont 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 TagHash 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 从单机缓存进化成真正能横向扩容的存储系统。实际落地时没有绝对的好坏架构只有匹配业务场景的方案。小数据量读多写少主从复制加哨兵完全够用业务高速增长、写并发高、容量未来会超出单机范围就尽早规划集群。希望这篇文章能帮你做出更合理的技术选型避免在架构升级路上重复踩坑。