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

资讯详情

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

从主从复制到Redis集群:突破单点写瓶颈的架构演进

从主从复制到Redis集群:突破单点写瓶颈的架构演进 先想清楚一个问题你在从库上执行SET命令Redis会怎么回应如果你真在线上这么干过大概率会看到一条报错(error) READONLY You cant write against a read only replica.这是Redis从库默认开启replica-read-only yes后的结果。也许你会想那我关掉这个配置从库不就能写了吗能写但别高兴太早。从库一旦可写主从数据同步时从库上的本地写入会被覆盖数据呈现一种不可预测的混乱状态。这正是很多人对主从复制的理解停留在“多个节点分摊读写压力”的错觉上——实际上主从复制解决的是“读的扩展”和“数据冗余”它从来没有解决“写的能力”问题。这也是为什么当业务量继续往上走团队总会遇到那个绕不开的追问已经有了主从复制为什么还要上Redis集群这篇文章想把这个话题拆开讲清楚。我会从主从复制的真实边界开始讲Redis集群到底补上了哪块拼图再说到数据分片和请求路由机制的底层逻辑最后给出从主从复制演进到Redis集群的迁移思路和落地建议。1. 主从复制真正解决的问题和它解决不了的问题1.1 主从方案让Redis第一次有了“副本”先回到原始场景。如果Redis只有单机节点最痛的点是什么第一机器挂了整个缓存层直接不可用。第二流量上来后单节点的CPU和内存迟早成为瓶颈。主从复制出现后这些问题改善了一半从库通过REPLICAOF命令或配置文件把自己变成主库的完整副本主库的数据变更通过RDB快照和增量命令传播同步到从库。这样一来Redis从“单点”变成了“主节点负责写从节点负责读”的基本拓扑。你可以部署一主两从、一主三从甚至一主多从的架构读请求可以打到从库上主库的压力降下来。同时如果主库出现故障从库可以晋升为新的主库业务恢复时间从“重新搭建Redis”缩短到“几秒钟内完成角色切换”。所以主从复制解决的是两个问题读流量的横向扩展以及数据的高可用冗余。1.2 但它从没解决“写能力”和“存储容量”的天花板接下来是重点。假设你现在有1个主节点和2个从节点。从节点的存在能分担读压力但你仔细想想——所有写请求仍然只会落在同一个主节点上。内存是有限的。一台机器就算配置很高内存也就几百GB。如果你的业务需要缓存几亿个key单机内存不够用主从复制一点办法都没有因为无论挂多少个从库它们都是同一个主库的完整副本数据量并没有被拆散。CPU也是一样。写请求在主节点上执行主节点的单线程模型决定了它一个时刻只能处理一个命令。虽然Redis 6.0之后引入了多线程IO处理但命令的真正执行还是集中在主线程。当写入QPS达到一定阈值主节点的CPU会成为瓶颈从库再多也分担不了。还有一个容易被忽略的点主从复制的故障转移并不自动。在现代Redis运维里通常是通过Redis Sentinel哨兵机制监控主节点状态并在主节点宕机时自动完成从库晋升。但如果你的架构是裸主从没有引入哨兵主库宕机后需要人工介入这个过程可能会持续几分钟甚至更久。就算有了哨兵主从结构仍然是一个“单写者”模型同一时间只有一个节点能接受写请求。这里有一个很关键的判断主从复制 哨兵架构适合的是数据量规模可控、写入压力尚未成为瓶颈的业务。它解决的是“高可用”和“读扩展”不是“写扩展”和“数据水平扩展”。当你的数据量上升到单机放不下或者写QPS开始逼近单节点的处理上限时真正要做的不是加从库而是想办法让数据分散到多台机器上让每台机器只处理一部分数据——这就是Redis集群要做的事。2. Redis集群引入后到底发生了什么变化2.1 核心不是“多几个节点”而是“一份数据分成多份”很多人以为Redis集群就是把主从复制多套化每套自己玩自己的。这个理解不能算全错但真的没有抓住重点。Redis Cluster是Redis官方在3.0版本引入的分布式解决方案它的核心机制是数据分片。整个集群把key空间划分为16384个哈希槽hash slot每个节点负责一部分槽位。写入时客户端会对key计算CRC16校验值再对16384取模得到一个槽位编号这个槽位由集群中某个节点负责。举个例子假设一个集群有3个主节点槽位分布大概是节点负责槽位范围主节点A0 - 5460主节点B5461 - 10922主节点C10923 - 16383你写入一个user:10001的key通过CRC16计算得到槽位可能是5000那么这个key只会落到主节点A上。主节点B和C不会存储这个key。这和主从复制有本质区别主从复制下每个节点都保存全量数据集群模式下每个节点只保存一部分数据。这个变化带来一个直接好处一台机器的内存放不下所有数据那就把数据拆成几份分散到多台机器上。单机内存上限不再是整个Redis的上限理论上整个集群的存储容量是所有主节点内存之和。2.2 集群里的“每个节点”仍然需要自己的主从副本这里有一个让新手容易混淆的点。Redis集群虽然把数据分片了但并没有抛弃主从复制。相反集群里每个主节点通常都会配一个或多个从节点用来做数据冗余和高可用。也就是说一个生产级的Redis Cluster结构是3个主节点负责数据分片每个主节点挂1个从节点主节点负责处理它所在槽位的读写请求从节点作为主节点的副本在主节点故障时接替角色。所以更准确地说Redis集群是“分片 主从复制 故障转移”三者合一的方案。主从复制在集群内部仍然承担着数据副本的角色但它已经不足以支撑整个分布式架构——因为它只解决单分片的高可用不解决数据在多节点之间的分布。2.3 从“一主多从”到“多主多从”读写能力同时扩展对比一下两种架构的写入能力主从结构1个写入口无论挂多少从库写的吞吐上限就是单个主节点的上限。集群结构N个主节点每个主节点都能接受写请求理论上写入吞吐上限可以接近单节点上限乘以节点数。所以集群带来的关键变化是从“单点写、多点读”变成“多点写、多点读”。如果你的业务读写比是10:1主从复制还能撑一段时间但如果某项业务每个请求都伴随写入主从复制的写瓶颈很快就会暴露。这时候集群不再是锦上添花而是解决问题的必经之路。3. 集群引入了分布式问题靠什么机制兜底3.1 请求路由客户端怎么知道key在哪个节点集群模式下客户端面对的不再是单个Redis节点IP而是一组节点。它怎么知道user:10001这个key应该发给谁这里有两种常见的路由方式第一种是客户端计算槽位。客户端拿到集群节点信息后本地维护一份“槽位到节点”的映射表计算key的CRC16后直接向对应节点发起请求。这是很多客户端库的做法优点是请求路径短性能高。第二种是节点转发。客户端随机连接任意节点如果key不在这个节点负责的槽位上节点会返回一个MOVED错误并带上正确节点的IP和端口。成熟的客户端会处理这个重定向自动向正确节点重新发送请求。这两个机制看起来很细但它们是集群可用的基础。很多人在集群模式下遇到性能问题往往是因为客户端没有正确开启集群模式导致大量请求走了重定向路径网络开销剧增。3.2 槽位和key的映射为什么是16384不是一个更大的数字这是另一个值得深挖的细节。Redis Cluster选择16384个槽位而不是65536或更多是经过权衡的。槽位越多节点间的数据迁移粒度越细但传输的元数据也会更大。每秒钟节点之间通过Gossip协议交换心跳信息这个信息会携带节点的槽位配置。如果槽位数量太大心跳包会变得很大网络开销上升。官方设计时认为16000个槽位对于实际节点规模已经足够。我们可以简单算一笔账一个集群最多上千个节点的话每个节点平均也能分到十几个槽位迁移粒度足够小而心跳包的大小又被控制在可接受范围内。这个机制在平时可能不显眼但它决定了集群能够支持的最大节点规模也决定了在线扩容缩容时数据迁移的颗粒度。3.3 数据迁移在线扩容不是加一台机器那么简单集群还有一个很重要的能力在线扩容。你在集群里加一个新主节点它加入后并不会自动拿到数据。你需要把已有节点的一部分槽位分配给新节点这个过程叫reshard重新分片。槽位迁移时Redis不是停机迁移的。源节点会把槽位上的数据逐步同步给目标节点迁移过程中阻塞只在很小的粒度内发生。但这不代表迁移没有成本和风险。实际操作时一次大规模reshard会占用主节点的CPU、内存和网络带宽。如果业务量本身已经很高迁移过程中可能出现请求响应变慢、网络拥塞等问题。常见做法是在业务低峰期做数据迁移控制迁移并发度监控主从节点间的同步延迟。这里要明确一个边界Redis Cluster的在线扩容能力是真实存在的但把它用好在生产环境需要配合完善的容量规划和运维手段。不是加一台机器集群就会自动变强壮。3.4 多key操作的代价你失去了一些单机时代的便利使用集群你还要接受一个变化跨槽位的多key操作变得麻烦了。在单机Redis里MGET、MSET、SUNION、RENAME这些命令很常见。但在集群模式下如果多个key不在同一个槽位这些命令会直接报错或者只能在同一个槽位下执行。要解决这个问题只能依赖hash tag机制在key里用花括号标记同一部分让需要同时操作的key通过这一部分计算出相同的槽位。比如user:{10001}:profile和user:{10001}:ordersCRC16计算时只针对10001部分所以它们会落在同一个槽位可以正常进行多key操作。这是一个典型的“分布式代价换容量和性能”的案例。如果你有很多跨实体的复杂操作设计key的时候就必须提前考虑hash tag的使用否则后期改造的成本非常高。4. 从主从复制演进到Redis集群哪些坑值得提前避开4.1 第一步想清楚你到底是为什么迁移不是所有场景都需要集群。如果你的业务只是读多写少数据量在单机内存可承受范围内主从复制配合哨兵已经足够。硬要上集群反而会引入更多运维复杂度和限制条件——比如多key操作的约束、客户端需要适配、迁移期间要暂停部分业务操作等。反过来如果出现下面这些信号就该认真考虑集群了单机内存已经超过50%估算半年内会达到上限写QPS已经逼近单主节点的处理上限业务要求缓存层具备水平扩展能力扩容时不能停机太久多个独立业务共享一套Redis互相影响难以隔离。简单说存储容量和写能力成为瓶颈时集群是更合理的解法只是读压力大优先加从库和哨兵。4.2 第二步迁移前先做容量评估和分片设计迁移之前你要对现有数据做一次盘点总数据量多大单key平均大小多少热点key是否集中在少数几个key上业务中多key操作比例有多高这些数据直接决定你要规划多少个主节点每个节点分配多少内存以及hash tag需要应用到哪些业务key上。常见的分片策略是先根据总数据量和目标单节点内存算出主节点数量。比如你有200GB数据计划每个主节点使用32GB内存那至少需要7个主节点考虑到主从复制每个主节点配1个从节点最终是14个节点。4.3 第三步先搭小集群验证再切生产流量迁移不建议做“一步到位”式切换。我更推荐先在测试环境搭建一套和线上配置接近的小集群一次性验证几个关键点客户端是否支持Redis Cluster协议连接配置是否正确业务里用的Redis命令在集群模式下是否全部兼容依赖多key操作的逻辑是否已经调整好hash tag批量操作是否有性能损失是否需要拆分请求数据迁移完成后旧缓存的热点key是否自动失效。这套验证流程看起来繁琐但它能帮你提前发现绝大部分兼容性问题。很多团队迁移集群后线上暴雷不是因为Redis本身不稳定而是因为业务代码隐式依赖了单机Redis的某些行为比如KEYS、SCAN全库扫描比如跨key事务比如RENAME。4.4 常见问题排查链路如果集群上线后出现异常可以按下面的顺序排查看客户端日志有没有MOVED、ASK重定向重定向频率高不高如果每个请求都在重定向多半是客户端没有开启集群模式或者槽位信息没有及时更新。看节点负载哪个主节点的CPU和内存明显偏高如果热点集中考虑对热点key做拆分或者让业务层增加本地缓存。看主从同步状态集群里的某个从节点同步延迟过大可能导致故障转移后数据不一致。看内存碎片率集群环境下数据分散在多个实例内存碎片可能会比单机模式更明显需要周期性执行MEMORY PURGE或者调整maxmemory-policy。看慢查询日志很多慢查询实际上是key设计不合理导致大key、热key在一个槽位上堆积拖慢了整个槽位的响应。4.5 集群建设不是一锤子买卖最后提一个经常被低估的点Redis集群上线之后还需要持续做容量管理。集群的数据分片逻辑决定了扩容缩容是一个动态过程不是加机器就完事。每次业务里程碑发布前最好都做一次容量预估同时集群的操作工具虽然官方有提供但生产环境通常还需要围绕它建设监控、告警、配置管理和自动化运维能力。一个完善的Redis集群运维体系至少包含这些每个节点的CPU、内存、网络、连接数监控槽位分布和节点状态的可视化数据迁移脚本和回滚方案灾备演练和故障转移测试客户端连接池参数和超时配置的规范。5. 一套可复用的选型判断框架写了这么多最后回到一个更高层的问题作为技术负责人你怎么判断当前该用主从复制还是Redis集群我整理了一个四步判断的框架可以直接用来做选型依据。第一步看数据量。单机内存能装下未来半年到一年的数据量优先用主从复制如果数据量已经超过单机内存上限或者增长趋势非常陡峭就要考虑集群。第二步看写入压力。读多写少且写QPS远未达到瓶颈主从方案通常够用如果写QPS已经占满主节点CPU或者可以预期未来会持续上涨集群是更好的选择。第三步看操作复杂度。业务中有大量跨key事务、复杂Lua脚本、全局SCAN扫描的场景主从复制和哨兵模式下更顺手集群模式需要改造同时会引入多key操作的限制。如果改造代价过大就要在数据扩容和代码改造之间做权衡。第四步看运维能力和团队建设阶段。小团队、业务快速迭代阶段主从复制加哨兵是最务实的选择部署简单、排障成本低。当业务进入快速增长期团队有精力搭建配套监控和运维体系时再推进集群建设成功率和收益都会更高。这套框架的核心逻辑是架构选型不是越复杂越好而是越能匹配当前阶段的需求越好。等你真正遇到单机写瓶颈和数据量天花板的那一天自然会理解主从复制和集群不是替代关系而是同一个演进路径上不同阶段的选择。写在最后回到开头的问题有了主从复制为什么还要Redis集群答案其实很简单主从复制让Redis从单点变成了高可用架构但它仍然是一个“单写者”模型写能力受限于单节点存储容量受限于单机内存。Redis集群通过哈希槽分片让数据分布在多个主节点上从根上突破了这两个天花板。但集群不是免费的午餐。它引入了新的复杂度数据分片、请求路由、在线迁移、多key操作限制、客户端改造、运维监控升级。选择集群的时机应该是在你的业务数据量和写入压力已经明确逼近主从方案上限的时候而不是因为“Redis集群听起来更高级”。希望这篇文章能帮你把Redis的架构演进路径想得更清楚。如果你正在规划缓存层架构不妨先按上面提到的方法做一次容量评估再决定是不是要走出下一步。
返回列表