面试这事问到Redis集群架构说实在的初级和高级的回答差得不是一星半点。刚入行的可能背背主从复制、哨兵这几个名词就完事了真正在生产环境摸爬滚打过的会从数据分片聊到槽位迁移从脑裂隐患聊到多副本一致性甚至能把扩容时的抖动都给你分析明白。这篇文章就把我这些年折腾Redis集群的完整心得做个复盘从基础架构到进阶实战一条线捋清楚下一篇回答直接用得上。1. 先从整体架构看Redis集群到底解决了什么问题1.1 单机时代的三个天花板Redis刚出道那会儿大家就是把它当缓存用单节点跑得飞快因为数据都在内存里单线程模型又省掉了上下文切换的麻烦。但任何一个系统膨胀到一定规模单机必然撞墙而且一撞就是三堵墙。第一堵墙是容量墙。内存是有限资源即便一台物理机插满内存条Redis能用的也就那么几十个G。数据量一旦超过这个水位要么淘汰要么停机扩内存都很憋屈。第二堵墙是性能墙。单线程模型决定了Redis的瓶颈不在CPU而在内存和网络带宽读写并发高到一定程度单节点的带宽就吃满了延迟随之抬升。第三堵墙是可用性墙。一台机器挂了整个缓存层直接瘫痪下游数据库瞬间被流量打穿这在生产环境是不可接受的。这三种痛点单独拎出来都好解决但要同时解决就需要一套分布式方案了。这就是Redis集群要干的事把数据打散到多个节点上每份数据还有副本兜底节点挂了能自动切换。一句话概括集群架构的目标就是横向扩容加高可用二者缺一不可。1.2 两条演进路径哨兵模式与Cluster模式很多人把哨兵和Cluster混为一谈其实这两条路解决的问题完全不同。哨兵模式解决的是高可用问题它的核心是监控主节点状态主挂了自动把从节点提拔成新主。但哨兵模式有一点很要命——所有节点保存的是同一份全量数据容量依然是单机的上限也就是说它只能解决“挂了怎么办”解决不了“存不下怎么办”。Cluster模式则是从根上重新设计了数据分布方式。整个集群被划分成16384个槽位所有key通过CRC16算法映射到某个槽位槽位再分配给不同节点。数据被真正打散到了多台机器上容量和性能都能横向扩展。两者不是替代关系而是互补关系。小规模场景用哨兵完全够用主从加哨兵三节点能扛住大多数中小业务的读写压力。数据量一旦达到几十个G甚至上百个G哨兵模式就撑不住了这时候才轮到Cluster上场。我见过不少团队一上来就上Cluster其实业务量根本到不了那个水位反而被槽位迁移和客户端路由搞得焦头烂额这就是架构选型没想清楚。2. 核心机制解析Cluster模式的数据分片与高可用设计2.1 槽位分配原理为什么是16384个槽Cluster的数据分片核心是哈希槽hash slot不是一致性哈希这是面试高频考点。Redis把整个keyspace固定分成16384个槽位每个key通过CRC16(key) % 16384计算出属于哪个槽槽位再由管理员或集群自动均衡分配给各个节点。很多面试官会追问为什么偏偏是16384而不是65536这是一个很有水平的细节。CRC16算法能产生65536个哈希值但Redis的协议头只有8位用于传输槽位信息8位最多表示256个值所以必须取模。如果取模到65536槽位信息需要占16位心跳包里多传的字节数在全网通信时就是不小的开销。16384是8位和16位之间的一个平衡点——槽位信息足够丰富同时协议包保持精简。还有一个更接地气的原因槽位数量要适配集群规模的动态变化。节点数少的时候每个节点分到的槽位数应该相对均匀。16384这个数在几百个节点的规模下依然能保持均衡。如果槽位太少比如1024个节点数一多某些节点可能会没有槽位或者分配极不均衡这个细节在生产环境调优时会很头疼。实际分配时关键点是每台机器上的槽位要尽量均匀。Redis提供了reshard命令做在线迁移可以指定从哪些源节点迁多少个槽到目标节点。迁移过程中key会在新旧节点间转移涉及到的客户端请求会收到ASK和MOVED重定向信号这是后面要详聊的内容。2.2 主从复制与故障转移从节点到底在干嘛Cluster模式下每个主节点可以挂一个或多个从节点。从节点不参与读写除非你手动开启从节点读它存在的意义就是热备。主节点挂掉后从节点会通过集群内部的选举机制成为新主节点。选举机制值得注意。整个集群中每个主节点都有一票从节点想竞选需要获得超过一半的主节点投票。这跟Raft算法的思路相似核心目的就是防止脑裂。主从切换还有一个集群纪元epoch的概念每一轮选举都有独立的纪元编号用来标识选举的新旧程度避免旧节点的消息干扰新主节点。生产环境中我踩过一个坑主节点宕机后从节点的数据可能滞后。如果主从复制延迟严重从节点接管后会丢失近期写入的数据。很多业务容忍不了这种丢失所以集群规模较大或写入量较高时要重点关注repl_backlog的大小和主从复制延迟指标。可以适当调大repl-backlog-size同时监控master_repl_offset和slave_repl_offset的差距。2.3 什么是“集群脑裂”以及如何规避脑裂是分布式系统里的经典问题。在Redis集群中它表现为主节点发生了网络分区但并没有真正宕机还在接收客户端写入同时部分从节点和主节点失联无法同步数据。这时候如果从节点被选举为新主原本的旧主又恢复了网络连接集群里就出现了两个主节点各自接受写入数据就分裂了。Redis官方应对方案是min-replicas-to-write和min-replicas-max-lag两个参数。简单说就是控制主节点写入的门槛——如果从节点数量不满足最低要求或者从节点数据落后的秒数超过阈值主节点就拒绝写入宁可牺牲可用性也不能让脑裂扩散。生产环境里这两个参数务必开启尤其在做跨机房容灾演练的时候。3. 进阶细节客户端路由、通信协议与数据迁移3.1 MOVED与ASK重定向客户端是怎么找到数据的Cluster模式下客户端拿到的是一份槽位与节点的映射关系表本地缓存一份。当客户端发送某个key的请求时先本地计算CRC16(key) % 16384然后直接访问对应的节点。大多数场景下这个节点就是正确的数据所在节点。但槽位迁移期间情况就复杂了。假设槽位100正在从节点A迁往节点B客户端发请求到节点A节点A发现自己已经没有这个槽了就会返回MOVED错误同时告诉客户端这个槽现在的归属是节点B。客户端收到MOVED后更新本地缓存并向节点B重新发送请求。这个过程中客户端逻辑很简单都是服务端说了算。还有另一种情况槽位迁移正在进行中部分key已经迁到了节点B但客户端还在请求节点A。节点A发现key不在自己这边但槽位还没完全迁移完成就会返回ASK错误。ASK和MOVED的区别在于MOVED是永久迁移客户端要更新映射缓存ASK是临时重定向客户端只需要当前请求去目标节点查一次不需要更新缓存。这个区别是面试里常见的陷阱问题。对于客户端而言现在主流的Redis客户端库比如Java的Jedis、RedissonGo的go-redis都已经内置了集群路由逻辑。选型时要注意一些老版本客户端对ASK和MOVED的处理并不完善迁移期间可能出现短暂的请求失败。升级到维护活跃的新版本是避坑的第一步。3.2 Gossip协议集群状态是怎么同步的集群节点之间靠Gossip协议通信每个节点定时向其他节点发送ping/pong消息交换自身掌握的集群状态。消息里携带的信息包括节点ID、槽位分配情况、主从关系、故障状态等。这种协议的好处是去中心化没有单点瓶颈坏处是状态传播有延迟特别是节点故障的感知可能要经过几个心跳周期才能被全员确认。所以集群节点数越多心跳包越大通信开销越高。这也是为什么Redis官方建议一个集群的节点数不要超过1000个。超过这个规模后Gossip消息的广播风暴会抢占业务带宽。实践中集群规模通常控制在几十个节点以内配合多集群分片的架构去扩展。集群节点间通信还有一个重要的细节默认的bus端口是客户端端口10000。生产环境配置安全组、防火墙时一定要把这个端口段也放开否则节点间通信会异常表现出来就是集群状态永远在resharding或者fail状态来回跳。这个坑我见过太多次每次排查都要花不少时间。3.3 在线扩容与缩容reshard实操要点Cluster模式的扩容流程如下新节点加入集群被标记为没有槽位然后通过reshard操作从其他节点迁移一部分槽位给它。迁移涉及的数据量可能很大期间这个槽位范围的读写请求会被重定向客户端感知到的是延迟变高或者短暂不可用。实操前一定要看两个数据每个节点当前的槽位数以及当前key的总数量。不要盲目平均分配槽位因为不同槽位的key数量可能差异巨大。举个例子槽位8000上可能有几百万个key槽位15000上可能只有几百个按槽位平均迁移会导致数据倾斜。合理做法是先扫描一下集群的key分布按实际数据量决定从哪些源节点迁出多少个槽。迁移过程中还要关注内存和带宽的余量。数据迁移本质上是把一部分key序列化后通过网络传输到目标节点源节点和目标节点的内存峰值都会升高带宽也会被占用。如果集群水位本身已经到了80%以上贸然扩容迁移很容易造成OOM。稳妥的做法是提前清理冷数据或者选择业务低峰期操作。还有一个容易被忽略的点迁移前要确认maxmemory策略。如果集群开启了内存淘汰迁移过程中源节点删除key、目标节点写入key的节奏不一致时可能出现数据短暂不一致。建议迁移期间临时把淘汰策略调整为不淘汰或者至少保证淘汰策略在全部节点上配置一致否则后续排查会很痛苦。4. 面试复盘高频追问与实战思路4.1 面试官最爱追问的四个问题第一个高频问题Redis Cluster模式下一个key的读写流程是怎样的回答时不要只背CRC16取模公式最好把客户端缓存槽位映射、MOVED重定向、ASK临时重定向这几个环节都讲完整再补充一个生产场景迁移期间延迟升高。能主动说出“迁移时会有重定向开销所以我们会至少留20%-30%的性能余量”这种话会很加分。第二个高频问题集群模式下事务和Lua脚本是怎么处理的这个必须讲清楚。Cluster模式下只有同一个槽位内的多个key才能保证原子性跨槽位的事务和Lua脚本默认是失败的。解决方法一般有两种一是用hash tag强制把相关key放在同一个槽位比如{user:1001}:profile和{user:1001}:orders花括号内的部分参与哈希计算这样两个key就会落入同一槽二是牺牲一致性和原子性通过业务层补偿机制去处理。能不能脱口而出hash tag的细节很能体现是不是真做过。第三个高频问题数据倾斜是怎么产生的生产环境最常见的倾斜原因就是这么几条首先是没有用hash tag某些大用户或热点key天然聚集在某个槽位范围其次是key的命名不规范比如时间戳前缀这种高位变化很快的因素导致数据在槽位上层层叠叠不均匀再就是某个节点上存在超大key比如一个包含几百万字段的hash结构操作时CPU和内存都吃紧。解决方案就是拆分大key、规范key命名、用hash tag打散。第四个问题集群模式下怎么做备份和恢复很多人只知道单机版本用RDB和AOF集群模式下会懵。这里要说清楚可以使用redis-cli --cluster backup命令对集群做全量备份但备份文件需要依次在多个节点上做聚合。更常见的方案是定期对每个主节点执行BGSAVE然后把RDB文件归档到对象存储。恢复时要注意必须先恢复所有节点再启动集群让节点之间通过Gossip协议重新确认彼此状态不能只起一个节点。4.2 生产环境实战一次扩容故障的排查复盘有一次大促前扩容集群从6个节点扩到9个节点迁移过程本身没报错但扩容完成后出现了一个诡异的性能问题部分请求延时从2ms飙升到200ms而且集中在几个特定前缀的key上。刚开始怀疑是网络抖动排查了一圈发现网卡流量正常CPU也正常。后来把慢请求日志拉出来一看发现这些key都集中在某个槽位范围里。再一查这个范围的key总数占全集群的40%左右——之前某个业务团队往Redis里存了一批带固定时间戳前缀的数据时间戳的小时部分变化慢高位取模后大量key落到了相邻的槽位里恰好被迁移到了同一个新节点上。这就导致新节点内存水位远高于其他节点内存淘汰触发频繁hit rate下降请求打到后端存储整体延迟链路上去了。解决方式不复杂把这个槽位范围的key重新hash tag用业务前缀加随机后缀打散。但重构key名意味着所有读路径和写路径都要同步修改还得做双写迁移前后折腾了一周才稳定下来。这个教训我印象很深——集群的数据分布是否健康不只是架构的事还和业务方的key设计习惯强相关。所以在业务规范层面做好key命名约束比事后调整成本低得多。4.3 一些值得重复的避坑经验第一Cluster模式会自动开启所有数据库的槽位分配但如果你还在用select命令选择多DB的业务上了Cluster后基本就废了。Cluster模式下只有一个DB多DB隔离的需求要用不同key前缀或者不同集群来解决。迁移前排查业务代码里有没有select这个动作虽然基础但非常关键。第二关于maxmemory-policy和淘汰策略。全集群所有节点的淘汰策略要保持一致否则同样的key在不同节点上的行为可能不同。尤其注意volatile-lru和allkeys-lru的区别选错了会在集群扩容、迁移过程中放大内存压力。第三客户端连接数。每个节点都有连接数上限而Cluster模式下客户端通常会和集群内所有节点建立连接。节点数越多客户端连接数越大。大促前要预留连接数水位否则扩容节点后连接风暴可能会把新节点打得直接拒绝服务。第四监控指标不要只看Redis自身。集群模式下节点之间的网络延迟、带宽利用率、包丢失率都是影响稳定性的关键因素。跨机房部署的时候节点间的RTT如果超过5ms集群状态变更的感知和收敛都会变慢脑裂风险也随之上升。5. 架构选型建议你的业务真的需要Cluster吗这是最后想说的也是我经常怼人的一点不要为了用Cluster而用Cluster。Cluster模式解决了容量和扩展性问题但它带来了运维复杂度的大幅上升。槽位管理、数据迁移、客户端路由、节点通信每一个环节都比单机和哨兵模式复杂。如果你的数据量在几十个G以内读写QPS在十万级别以下单机Redis加哨兵模式完全够用而且稳定性更好。Cluster真正的用武之地是内存容量上限直接限制了单机或者单集群的处理能力或者读写流量已经把一个主节点的带宽打满了。这两个条件至少要满足一个才是合理的Cluster演进时机。如果确定要上Cluster方案上我建议分两步走。第一步先按业务维度拆分成多个逻辑分区每个分区一个独立集群而不是把所有业务揉进一个大集群。一个集群内混合了缓存、消息队列、排行榜、计数器等各种业务互相争抢内存和CPU出问题的时候爆炸半径也会更大。第二步在集群内部合理设置槽位和副本按业务重要程度给核心数据多挂几个副本非核心数据保持单副本或双副本成本和可用性之间取平衡。我个人的体会是集群架构的项目80%的时间不是花在配置和部署上而是花在容量规划、数据分布设计、异常演练和监控告警上。很多团队上集群后最不适应的不是Redis本身而是原先单机上可以“拍脑袋”的事集群里都要变成成体系的流程。这个转变想清楚了Redis集群这关就算真正过了。最后再分享一个小技巧无论用什么模式上线前一定要做一次主节点宕机的故障演练。具体做法很简单运维窗口期手动执行shutdown或者直接封禁某台机器的网络端口观察从节点能否在预期时间内完成切换客户端能否自动恢复。这个演练做完你不光是对集群机制有底气面试时候聊故障恢复也能聊出跟别人不一样的东西。
阅读完成 · 觉得有帮助?