尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Redis主从复制还是Cluster?容量与写扩展才是分水岭

Redis主从复制还是Cluster?容量与写扩展才是分水岭 很多人学完 Redis 主从复制就会产生一个疑问主节点挂了能从节点顶上读压力大了还能挂从节点分担那为什么还要 Redis Cluster这个疑问在面试里经常出现实际项目里也经常被误用。先说结论主从复制解决的是高可用和读写分离问题Redis 集群解决的是容量扩展和写扩展问题。两者解决的问题不一样所以不是“谁替代谁”而是不同层次的事。如果只做主从复制而不上集群在单机内存和单点写能力到达上限时照样会卡死。下面从原理、场景和实操选择三个方面完整拆一遍。1. 先搞清楚主从复制到底解决了什么问题1.1 主从复制的本质是数据冗余加读写分离Redis 主从复制的基本结构是一个主节点带多个从节点。主节点负责写请求从节点负责读请求数据从主节点异步同步到从节点。这个架构解决了两类问题第一是单点故障。主节点挂了从节点可以顶上前提是你手动切换或者部署了哨兵机制。没有哨兵的话主从复制本身不会自动把从节点升级成新的主节点这一点要特别注意。第二是读压力。如果你的业务是典型的读多写少比如缓存热点数据、商品列表、用户会话那么一台主节点扛不住大量读请求时多加几个从节点把读流量分散下去效果立竿见影。从这个角度看主从复制是 Redis 高可用体系的第一层防护成本低、配置简单、理解容易。很多小团队从单机升级到主从复制稳定性已经提升了一大截。1.2 主从复制的两个硬边界单机容量和单点写能力但主从复制没有改变一个事实所有写请求仍然集中在主节点上。无论你挂了多少个从节点写入路径始终只有主节点一条。这带来两个硬边界。第一个边界是容量。主节点是单台机器内存上限受物理资源限制。一台 64GB 内存的机器就算配了 Redis 的 maxmemory实际能用的数据空间也就是 50GB 到 60GB 左右。当业务缓存数据超过单机内存时主从复制解决不了扩容问题。你加从节点从节点的数据也是从主节点全量复制过来的总量并没有拆分。第二个边界是写吞吐。主节点要承接所有写命令、处理持久化、再把数据同步给所有从节点。如果写请求本身就到了每秒几万甚至十万级别主节点的 CPU 会率先成为瓶颈。此时加从节点分担不了写压力反而会增加主节点的同步开销。我在实际排查过一个项目主从配置看起来没有问题但主节点 CPU 经常冲到 90% 以上。后来发现写 QPS 已经接近单机上限从节点读流量又大主节点一边处理写请求一边全量或增量同步到多个从节点网络和 CPU 双双被打满。这种情况就是典型的主从架构已经到边界的信号。注意主从复制解决的是“单点故障”和“读扩展”不解决“容量扩展”和“写扩展”。判断是否需要集群先看这两个指标有没有逼近上限。2. 主从复制扛不住的三类真实场景2.1 数据量超过单机内存时主从复制无能为力很多团队把 Redis 当成缓存用以为设置了过期时间就不会有内存问题。但实际线上经常出现两种情况。一种情况是热点数据太多内存不断膨胀。比如做了商品详情缓存、用户行为缓存、配置缓存每一个 key 都设置了过期时间但总量仍然超过单机内存。此时 Redis 会用淘汰策略删除不常用的数据导致缓存命中率下降大量请求穿透到数据库。另一种情况是数据必须全量留存不能随意淘汰。比如做实时排行榜、分布式锁记录、限流计数部分 key 不能设过期时间。这时候内存压力会持续增长主从节点都保存同一份全量数据等于一台机器装不下再多从节点也帮不了忙。要解决容量问题必须把数据分散到多台机器上。每个节点只存一部分 key这才叫分片。Redis Cluster 做的事情就是分片。所以当你的数据集预期会超过一台机器内存时主从模式就不够用了。2.2 写请求成为瓶颈时加从节点反而帮倒忙读多写少的场景下加从节点分担读流量很有效。但写多读少的场景下比如秒杀扣减库存、热点计数、消息队列、实时流计算写 QPS 很高主节点的 CPU 会成为瓶颈。更麻烦的是主从复制是有同步开销的。主节点处理每个写命令之后还要把命令传播到所有从节点。从节点越多主节点的网络输出和同步成本越大。在高写入压力下多加从节点不仅不能分担主节点压力还可能让主节点的延迟明显上升。我建议判断是否需要集群时重点看两个指标写 QPS 是否超过单机上限主节点 CPU 是否长期在 70% 以上。如果两个条件同时成立就该考虑把写流量也分散到多个节点上。Redis Cluster 的分片机制让每个主节点只负责一部分 key。写请求会根据 key 的哈希结果路由到对应节点每台机器承担的写压力就降下来了。这是主从复制做不到的。2.3 故障切换要求高时主从复制不是完整的高可用方案还有一个容易被忽略的问题。纯主从复制模式下主节点发生故障后从节点不会自动接管需要人工介入。即使配合哨兵模式故障切换过程中涉及选举、配置更新、客户端重连整个过程也不是零配置的。哨兵解决了自动故障切换的问题但在数据量和写压力已经很大的场景下即使切换成功新的主节点还是单机写能力容量和性能瓶颈依然存在。哨兵只是保证可用性不保证扩展性。集群模式下故障转移是自动的。某个主节点挂了它下面的从节点会通过选举升级为新的主节点客户端请求会重新路由到新节点。整个集群仍然有多个主节点在对外服务单点故障的影响面更小。3. Redis Cluster 到底比主从复制多做了哪些事3.1 数据分片把 key 分散到多个主节点Redis Cluster 采用哈希槽机制整个集群有 16384 个槽位。写入一个 key 时客户端通过对 key 做 CRC16 计算再对 16384 取模得到这个 key 属于哪个槽然后路由到对应的主节点。集群默认配置下每个主节点负责一部分槽位。比如 3 个主节点的集群槽位分配可能是 0 到 5460、5461 到 10922、10923 到 16383。这样每个节点只保存全量数据的一部分。数据分片带来了两个直接收益第一容量可以水平扩展。数据量大了往集群里加主节点重新分配槽位每个节点的内存压力就会下降。整个过程不需要停止服务。第二写压力被分散到多个节点。每个节点的写命令只涉及它负责的槽位单节点的写负载上限被抬高。有一个细节需要注意Redis Cluster 的最小规模通常是 3 个主节点。原因是槽位要尽量均匀分配而且故障转移时需要投票选举至少 3 个节点才能形成多数派。如果只搭两个主节点某个节点故障后可能无法完成可靠选举。3.2 自动故障转移从节点升级和多节点投票Redis Cluster 的每个主节点都可以配置从节点。主节点故障后集群会检测到节点不可达然后从该主节点的从节点里选举一个升级为新的主节点。选举过程依赖集群内部节点之间的心跳消息。Gossip 协议会持续交换节点状态当某个主节点在一段时间内无法被大多数节点访问就会被标记为故障。集群会从它的从节点中选出优先级最高的提升为新的主节点并接管原来的槽位。这个机制比哨兵模式更贴近 Redis 自身的数据结构。哨兵是独立于 Redis 的外部进程需要额外部署和维护。集群的故障转移逻辑内建在 Redis 节点中不需要额外组件但复杂度转移到了配置和运维层面。3.3 客户端路由MOVED 和 ASK 的处理逻辑集群模式下客户端不能像单机那样随意连接任意节点执行命令。客户端需要知道每个 key 对应的槽位由哪个节点负责。当客户端向错误的节点发送指令时节点会返回一个 MOVED 错误告诉客户端正确的节点地址。比如执行SET user:1001 name zhangsan结果槽位不在当前节点节点会返回MOVED 12780 192.168.1.10:6379客户端需要重新连接后再执行。ASK 错误出现在槽位迁移过程中。当集群在做扩容或缩容时部分槽位可能正在从旧节点迁移到新节点。客户端访问一个正在迁移的槽位旧节点如果已经查不到数据就会返回 ASK要求客户端转向新节点查询。所以生产环境使用集群一般不推荐自己解析 MOVED 和 ASK而是直接用官方推荐的客户端比如 redis-py-cluster、JedisCluster、Lettuce。这些客户端内置了槽位缓存和重定向逻辑用户只需要配置节点列表其余由客户端处理。注意如果你的客户端不支持集群协议即使服务端搭好了集群也会频繁遇到跨节点读写失败的问题。选客户端时先确认它是否支持 Redis Cluster 模式。4. 主从复制和集群不是二选一集群内部也有主从4.1 Cluster 的每个分片本身就是一个主从结构很多人会把主从复制和集群对立起来觉得项目要么做主从要么做集群。实际上 Redis Cluster 内部仍然依赖主从复制。集群里的每个主节点都可以挂一个或多个从节点。主节点负责处理该分片的读写请求从节点作为主节点的数据副本。主节点故障后从节点接管并变成新的主节点。所以集群模式并没有抛弃主从复制而是在主从复制之上增加了一层分片路由。我更喜欢把 Redis Cluster 理解成“多组主从复制组成的分片架构”。每一组主从解决局部高可用问题整个集群解决全局容量和写扩展问题。两者是叠加关系不是替代关系。4.2 主从模式适合什么规模集群模式适合什么规模如果业务处于以下阶段主从模式完全够用数据总量在单机内存的 50% 到 60% 以内未来两年不会翻倍。写 QPS 在单机承受范围内比如一秒钟几千到一两万。读压力可以通过增加从节点横向扩展。团队没有专门的 Redis 运维经验追求简单可靠。如果出现以下信号就该考虑集群数据量已经超过单机内存或者预计半年内超过。写 QPS 接近单机上限主节点 CPU 长期高于 70%。需要不停机扩容。单个业务故障的影响面要求尽量小不能在主节点挂掉后停写太久。具体量化数字会因机器配置不同有差异但思路是一致的主从模式适合“数据总量受控、写流量平稳”的阶段集群模式适合“数据量持续增长、写并发明显走高”的阶段。4.3 集群最少需要几台机器配置怎么规划Redis Cluster 最简配置是 3 个主节点没有从节点。这个配置可以满足集群的基本功能但高可用没有保障。某个主节点挂了它负责的槽位就没有节点接管这部分数据会不可用。更稳妥的配置是 3 个主节点加 3 个从节点每个主节点挂一个从节点。一共 6 个 Redis 实例可以部署在 3 台物理机或虚拟机上每台机器跑一主一从。这样做的好处是某一个物理节点挂掉集群可以跨机器完成故障转移。如果是小规模试验也可以用一台机器跑多个实例分别映射不同端口。不过这只适合学习验证生产环境不推荐因为单机多实例没有解决物理机故障问题数据都落在同一块磁盘上。5. 从主从复制迁移到集群时最容易被坑的几个点5.1 多 key 操作在集群里变成了受限操作这是从主从迁移到集群时最明显的差异。主从模式下所有 key 都在同一个 Redis 实例上MGET、MSET、SUNIONSTORE、事务块、Lua 脚本可以轻松操作多个 key。集群模式下不同 key 可能落在不同节点上。客户端对多个 key 执行跨槽操作时如果这些 key 的槽位分布在多个主节点上Redis 无法在一个节点上完成原子性操作。结果就是部分命令不再直接可用或者需要满足特殊条件才能在同一个节点上执行。解决办法之一是使用 hash tag。Redis Cluster 在计算槽位时如果 key 里包含花括号比如user:{1001}:name和user:{1001}:age哈希计算只针对花括号里的内容。只要花括号里的值相同这两个 key 就会落在同一个槽位也就能够在同一个节点上做多 key 操作。但 hash tag 不要滥用。如果把大量 key 都放在同一个 tag 下会导致数据集中在一个节点上破坏分片的均匀性。一般只对确实需要原子操作的关键数据使用。5.2 客户端连接方式和命令行为发生变化主从模式下客户端通常连接到主节点写、从节点读。集群模式下客户端需要连接到集群节点列表客户端会自动感知集群拓扑。有的客户端需要配置节点发现地址有的客户端支持自动发现并更新槽位映射。配置不当会导致连接失败或请求重定向过多。常见问题是只填了一个节点地址而且该节点挂了客户端没有其他节点可以连接导致整个应用报错。建议至少配置两个或三个节点地址提升容错性。另外SELECT命令在集群模式下也是不可用的。集群模式只支持 db0因为数据分片和数据库编号是两个维度的概念集群没有办法保证非 db0 数据库的数据均匀分布。迁移到集群前先检查代码里有没有使用SELECT 1这类操作。5.3 数据迁移不是简单的复制和切换从主从模式迁移到集群不能直接把 dump.rdb 文件丢到集群节点上启动。集群模式下每个节点只保存一部分槽位数据节点启动后会检查自身槽位和集群槽位分配是否一致。正确的迁移思路是先配置好集群让集群运行起来再通过数据同步或复用主从同步机制把数据导入。实际操作时可以使用 Redis 官方推荐的工具或者中间件完成迁移但要提前做好验证。我做迁移时习惯分四步走先在测试环境搭一套同样配置的集群模拟全量数据写入。使用客户端做数据双写或离线导入验证数据一致性。观察集群槽位分布是否均匀各节点内存占用是否接近。确认无误后在低峰期切换读写流量。不要在生产环境直接做“一把梭”式迁移尤其是数据量超过几十 GB 时先做容量评估和槽位规划再执行操作。5.4 Pipeline 和事务在集群下的行为变化Pipeline 在集群模式下仍然可以用但要注意同一个 Pipeline 里的多个 key 可能落在不同节点上。客户端在发送 Pipeline 时会先计算每个 key 的槽位然后把相同槽位的命令打包发到对应节点。不同节点上的 Pipeline 会被拆成多条请求。事务方面MULTI和EXEC只保证单个节点上的原子性。如果事务里的多个 key 分布在不同节点Redis Cluster 无法保证这些操作的全局原子性。Lua 脚本也有同样的限制。需要事务保证又必须跨节点时可以考虑业务层补偿机制或者换用支持分布式事务的存储方案。不要指望 Redis Cluster 能解决跨节点原子操作问题。6. 如果没有一上来就上万 QPS其实不一定要急着上集群6.1 判断是否需要集群的量化标准很多团队一听到“性能瓶颈”四个字就想着上集群。但集群引入之后客户端配置、数据分片、运维监控、扩容收缩复杂度都比主从模式高出一个量级。我更建议用几个量化标准来做判断数据量当前数据总大小是否接近单机内存的 70%。如果是且增长趋势明显准备上集群。写 QPS主节点写 QPS 是否长期超过单核处理能力。Redis 单线程模型下写命令密集时 CPU 会成为瓶颈一般几万写 QPS 就需要密切关注。读 QPS读请求是否可以通过增加从节点解决。如果从节点已经加到 3 个读压力仍然高说明单组主从架构已经顶不住考虑集群。故障恢复时间主节点挂掉后业务是否允许几分钟的不可用。如果要求秒级恢复集群的自动故障转移更合适。这些标准没有绝对数字因为机器配置、命令复杂度、key 大小都会影响结果。但思路是通用的先量化当前压力再判断架构是否需要升级。6.2 单主多从已经把大多数缓存场景扛住了很多企业级应用其实并没有到必须上集群的阶段。比如典型的电商缓存场景商品详情、用户信息、配置数据总量在 10GB 到 20GB 之间读 QPS 几千到上万。这种场景下一个主节点加两个从节点配合哨兵做自动故障切换已经足够稳定。多从节点分担读流量主节点专注于写请求哨兵负责故障切换。这套组合比直接上集群简单得多部署、监控、排查成本都要低。如果你是在学习阶段可以先用一个主节点加两个从节点做实验把复制原理、全量同步、增量同步、故障切换全搞清楚。然后把主从复制迁移到集群你会更容易理解为什么集群需要槽位分配、为什么多 key 操作受限。6.3 如果你的场景真的需要集群执行顺序是什么如果真的需要上集群我建议按下面顺序推进先评估当前数据量确定集群需要多少个主节点。一般从 3 个主节点起步。规划物理机或虚拟机分配每个主节点至少配一个从节点。搭建集群并确认槽位分配均匀。配置客户端使用支持集群模式的客户端连接。做功能测试重点检查多 key 操作、Pipeline、事务、Lua 脚本的兼容性。执行数据迁移先迁移测试数据再迁移全量数据。低峰期切换流量观察一段时间确认稳定。建立监控关注各节点内存、CPU、网络、命中率和故障转移事件。最后留一个个人判断如果主从复制还没有触及单机容量或写吞吐的边界不要为了追新而上集群。等真的出现容量写不下或者写压力打满主节点时再按上面的顺序做平滑迁移。架构升级不应该是提前焦虑而是发现明确瓶颈之后做出的自然选择。
返回列表