Redis集群原理与实战:从数据分片到故障转移的完整指南
发布时间:2026/9/18 12:12:09 作者:尧图编辑部 阅读量:1,286

单机Redis用到了25G内存的时候全量RDB每次要压十几分钟慢查询和内存淘汰在高峰期同时爆发值班电话凌晨两点被打爆。那段时间我几乎把社区里所有和Redis扩容相关的方案都翻了一遍最后老老实实把集群这块从原理到落地啃了一遍。这篇文章不是Redis Cluster的官方文档翻译是我自己从单机到集群、从踩坑到稳定运行之后沉淀下来的理解尽量把每个机制背后的为什么讲清楚。如果你正准备上集群、在准备Redis面试或者集群偶尔出点奇怪问题不知道怎么排查这篇应该能帮你省不少时间。我不打算只罗列怎么搭集群的步骤重点放在原理数据怎么切片、节点怎么通信、主从怎么切换、客户端怎么找到key、脑裂为什么会发生。把这些串起来之后再去看配置和命令基本就是水到渠成的事。1. 集群到底解决了什么先分清扩容和高可用两码事1.1 单机撑不住的四种表现很多人一听到Redis集群第一反应就是内存不够了要多搞几台机器存数据。这个理解不算错但太粗了。我在生产环境里总结下来单机Redis撑不住通常有四种表现它们对应的解法其实并不完全一样。第一种是内存容量触顶。数据量到几十个G之后成本先不提持久化的压力会非常明显。RDB fork子进程写快照内存越大fork瞬间的卡顿越明显如果开启了AOF重写时的磁盘IO和CPU消耗同样吓人。第二种是单线程CPU饱和。Redis 6之前核心命令是单线程处理的就算你的机器是128核一个实例也只能吃满一个核。热点key集中在某个高QPS业务上时CPU先到瓶颈。第三种是写请求的并发量超出单机上限比如某个活动场景下每秒几万次写单机不管怎么调优都有天花板。第四种是单一故障点机器挂了整个缓存层就塌了。这四种问题前三种本质上是算力不够、容量不够要靠分片来横向扩展第四种是可用性不够要靠副本加自动切换来解决。Redis Cluster的设计目标就是用一套方案同时覆盖这两类需求。1.2 哨兵加主从为何走了弯路在没有Cluster之前大家最常用的高可用方案是主从复制 Sentinel哨兵。主从复制解决的是数据冗余和读扩展Sentinel解决的是主节点故障时的自动切换。这套方案在小规模场景下非常成熟我早期好几个项目都是这么跑的。但它最大的问题在于数据还是只在主节点上写。从节点只是备份和分担读流量并没有真正把写容量拆开。一旦数据总量超出单机上限或者写QPS打满单核CPUSentinel再强也救不了你因为所有写请求还是落在同一个单点上。所以后来的演进路线很自然既然单机写有瓶颈那就把数据拆成多份分别放在不同的机器上每份数据由一个独立的主节点负责。这就是分片sharding的思路。而Redis Cluster做的事情是在分片之上又加了主从复制和自动故障转移让每个分片都不是单点。1.3 集群与哨兵架构的分工边界现在经常能看到有人纠结到底用Sentinel还是Cluster。我比较认可的分工方式是如果你的数据量单机装得下只是想要高可用Sentinel够用如果数据量已经逼近单机上限或者写QPS已经压满单核直接上Cluster不要再叠Sentinel。为什么不建议集群套哨兵因为Cluster内部已经内置了故障转移机制再套一层Sentinel反而让架构变复杂主从关系切换的决策者变多了容易出现两个主节点同时在线的风险。Cluster模式下的主从切换是由多个主节点投票决定的和Sentinel的quorum机制原理类似但它在集群内部完成管理成本更低。我实际迁移过的一个项目一开始是3主3从用Sentinel管后来数据涨到单实例30G写QPS也一直在涨最后迁移到了Cluster。整个迁移过程最大的感受是Sentinel管的是谁是老大Cluster管的则是数据放在哪一片 每一片谁说了算。两者解决的问题域不同但Cluster的设计是向下兼容的每一片内部仍然是主从复制。2. 数据分片与slot设计集群最核心的数学2.1 从哈希取模到slot的演进逻辑Redis Cluster分片的核心不是一致性哈希而是slot槽。这个概念很多人初听觉得绕其实背后的演进逻辑很清晰。早期搞客户端分片比如Twemproxy或者Jedis Sharded最常用的方式是哈希取模hash(key) % NN是节点数。这个方案实现简单但有一个致命伤节点数一变N变了几乎所有key的映射位置都会变化这意味着大量的key需要重新分布缓存会在扩容瞬间大面积失效对线上是个灾难。后来大家开始用一致性哈希它把哈希值空间首尾相接形成一个环每个节点映射到环上key沿环顺时针找到最近的节点。好处是增减节点时只需要迁移少量key坏处是实现和运维复杂度上来了而且节点少时数据容易倾斜一种宽松的做法是加一层虚拟节点。Redis设计集群时没有走这条老路而是引入了一个中间层把整个哈希空间划分为固定数量的slot。slot这个中间层最大的价值是把key到节点的映射拆成了两步key - slot是固定不变的slot - 节点是可以动态调整的。扩容缩容时只需要在节点之间搬运slot不用关心每个key单独是谁的。这个抽象非常像操作系统里的虚拟内存应用面对的是连续的虚拟地址空间物理内存页怎么分配是内核说了算。2.2 CRC16与16384这两个数字为什么重要在Redis Cluster里一个key最终落到哪个slot由下面这行计算决定HASH_SLOT CRC16(key) % 16384CRC16是循环冗余校验算法输入一个字符串输出一个16位的整数范围是0到65535。再对16384取模就得到了0到16383的slot编号。这里有同学会问为什么用CRC16而不是MD5或者SHA因为CRC16计算速度快、输出长度短作为key的散列函数足够均匀。Redis作者选的是CRC16的一个特定多项式实现社区里有人验证过对大量常见key的分布接近均匀。你不需要记住多项式细节只需要知道任意一个key都能被快速算出一个0到16383之间的slot编号这个编号就是它在集群里的地址。如果key里包含{}规则会变HASH_SLOT CRC16({}内部内容) % 16384。比如{user:1001}.name和{user:1001}.age会落到同一个slot。这个特性叫hash tag是做批量操作的关键工具后面专门讲。2.3 为什么要设为16384个槽而不是更多很多人背过Redis集群有16384个slot但很少人想过为什么偏偏是16384而不是65536或者干脆用2^32个槽这个数字背后是有工程考量的。第一心跳消息的大小限制。节点之间通过Gossip协议互相传播信息消息里要携带当前节点的slot分布信息。Redis用一种非常紧凑的方式表示bitmap位图一个bit代表一个slot是否由本节点负责。16384个slot只需要16384 / 8 2048字节也就是2KB。如果slot数量翻4倍变成65536位图就变成8KB。Gossip消息是在节点之间周期性传递的消息越大网络带宽消耗越大尤其集群规模上来之后这个开销会非常可观。第二数据分布粒度的成本。slot越细每个slot包含的key越少迁移时越灵活但迁移和管理的元数据开销也会增加。16384这个数量级对于Redis官方预期的千节点以内的集群规模来说既能保证key分布相对均匀又不会让slot管理和迁移成本失控。Redis官方在设计说明里也提到过节点数量一般不会超过100016384个slot在这个规模下每个节点平均能分到16个以上足够用了。第三还有一个不太起眼的点slot数量必须是2的幂这样取模操作可以用位运算优化。16384是2的14次方CRC16(key) 16383和% 16384是等价的但前者的计算开销更低。2.4 扩容和缩容时slot是怎么迁移的理解了slot之后扩容缩容就很好解释了。给集群加一个节点本质上就是新节点先以空master身份加入然后用reshard命令从老节点那边匀一部分slot过来。移动slot的过程并不是一个原子操作而是逐个slot、逐个key地迁移。这个过程中Redis做了几个精巧的设计来保证客户端无感。迁移一个slot时源节点和老节点都会保留这个slot的路由信息。客户端访问到源节点时如果key还没迁移走正常处理如果key已经被迁走了源节点会返回一个ASK重定向错误告诉客户端你去目标节点问。关于MOVED和ASK的细节我后面单独用一章展开。缩容的操作逻辑刚好反过来先把这个节点负责的所有slot迁移给其他节点等它变成空master之后再执行del-node把它从集群中移除。千万不要直接kill掉一个还持有slot的节点那会让集群负载不完整轻则部分key不可访问重则整个集群拒绝服务取决于cluster-require-full-coverage配置。3. 节点通信机制Gossip协议如何维持集群认知3.1 两个端口背后的设计意图和单机Redis不同集群模式下的每个节点会同时监听两个端口。一个是客户端端口默认6379另一个是集群总线端口默认是客户端端口加10000也就是16379。当初第一次部署时我还忽略了这个细节结果单机多实例部署时把两个节点都配成6379直接冲突启动失败。为什么要单独开一条总线因为客户端访问的流量和节点间内部通信的流量它们的优先级和特性不一样。客户端命令需要低延迟、高吞吐而节点间心跳、数据同步、故障广播走的是独立的连接这样不会因为大量客户端请求把心跳阻塞了导致节点被误判为下线。集群总线端口承载的是二进制的Gossip协议不是普通RESP协议。3.2 心跳消息里的节点信息交换Gossip协议是分布式系统里久经考验的通信方式Redis Cluster的节点之间每隔100ms就会互相交换一次心跳消息。这里互相交换不是全连接广播而是每个节点会随机挑选一部分节点发送PING收到PING的一方回PONG。消息里携带什么内容除了发送者自己的状态信息还有它从别人那里听到的多个节点的状态信息比如某个节点是否疑似下线、它的config epoch是多少、它负责哪些slot。这样设计的好处是信息传播不依赖中心节点任何一个节点挂了或者出现网络分区其余节点能通过多条路径逐渐达成一致的认知。代价是信息传播有延迟节点状态在全集群收敛需要一定时间。这就是为什么集群对故障的反应不会像单机那么瞬时而是以cluster-node-timeout为基准的秒级、十几秒级。Gossip在集群规模小的时候看起来有点浪费——明明只有3个节点每次心跳消息还要捎带那么多样本。但节点数涨到几十上百之后这种去中心化的设计优势就出来了每个节点不需要维护到所有其他节点的长连接也能在若干跳之内把关键状态扩散到全部节点。3.3 从PFAIL到FAIL故障状态如何确认这里要区分两个状态PFAILprobable fail疑似下线和FAIL确定下线。一个节点超过cluster-node-timeout默认15秒没有响应心跳首先会被标记为PFAIL。注意这只是我看到它可能挂了不代表整个集群都这么认为。PFAIL状态会通过Gossip消息扩散给其他节点。当集群里持有slot的主节点中超过一半都认为这个节点是PFAIL时它就会被打上FAIL标记。FAIL标记才是真正官方认定的故障会广播给所有节点触发后续的从节点选举。这个两阶段确认机制的用意很明显避免单节点因为一次网络抖动就误判整个集群状态。比如某个节点只是GC停顿了20秒它与其他节点的连接全部超时如果单节点就能判它死刑那么正常的节点很可能被误切换。PFAIL到FAIL的过半确认机制本质上是对网络抖动的一种容忍。3.4 已经是集群了为什么还需要主从复制很多第一次接触Cluster的人会问节点之间都用Gossip通信了slot也分了那主从还有存在的必要吗答案是分片管的是数据的水平拆分主从复制管的是一片的稳定可用。每个slot最终只由一个master负责处理写请求但这个master如果宕机了slot的数据不能跟着消失。所以每个master都会挂一个或多个replica从节点。从节点通过异步复制实时同步主节点的数据平时不承担slot的写请求只负责备份和读取扩展。当master被标记为FAIL之后副本中才会通过选举产生一个新的master接管整个slot集合。从这个角度看Redis Cluster其实是分片 主从复制 自动故障转移三者的结合体。任何一个master挂掉只要它下面还有存活的从节点整个集群就还能正常服务。这也是为什么生产环境的Redis集群至少会做成3主3从而非纯3主。4. 主从切换与自动故障转移一次failover的完整时间线4.1 从节点何时发起选举节点被标记为FAIL之后接下来就要看从节点怎么上位了。并不是FAIL一发生从节点就立刻抢着当老大。从节点在发起切换之前必须先确认自己有条件接替。具体来说从节点会检查几个条件它的master是否已经处于FAIL状态它自己与master断开连接的时间是否超过了cluster-node-timeout乘以一个系数防止主从之间短暂网络抖动就触发切换它的数据是否足够新。最常用的判断是复制偏移量如果从节点落后主节点太多即便切换上去也会丢失大量数据这种情况下它大概率不会抢着上位而是等数据集补上来之后再做决定。从节点发起选举的方式是把自己当前的config epoch加1然后向所有持有slot的主节点发送投票请求。config epoch可以理解成这个节点对集群配置的版本号版本号大的配置会覆盖版本号小的这在后面脑裂场景里非常关键。4.2 选举投票的规则与过半的意义投票规则上Redis集群用的也是过半思路每个持有slot的主节点在同一轮选举里只能投一张票从节点拿到超过半数主节点的投票就能当选新的master。举个例子一个3主3从的集群三个master分别是A、B、CA宕了A的从节点A1发起选举。B和C都有投票权A1只需要拿到2张票超过3的一半就获胜。如果一个master下面有多个从节点同时发起选举那么第一个取得过半票数的胜出其他从节点就保持从属状态。这个过半设计能保证同一轮选举中不会同时出现两个新master。因为两个候选者不可能同时拿到过半票数这在统计学和逻辑上都不成立。分布式的世界里不怕慢就怕多个决策者同时认为自己是老大所以任何需要用投票决出的位置过半都是最常见的安全阀。4.3 一次真实故障转移的节点视角时间线我刚开始学习时老是搞不清楚故障转移到底花了多长时间后来画了一条时间线才彻底明白。以默认cluster-node-timeout为15秒为例0s主节点A突然宕机或是网络断开。0s~15s其他节点持续向A发心跳没有收到PONGA被标记为PFAIL。15s之后A的PFAIL状态开始通过Gossip扩散其他主节点收到这个消息纷纷确认自己也无法联系A。当超过半数的持有slot的主节点达成一致A被标记为FAILFAIL消息广播到全部节点。FAIL之后A的从节点A1检测到master状态为FAIL发起选举请求其他主节点投票。选举完成后A1收到过半票数执行SLAVEOF NO ONE把自己升为master接管A原本负责的所有slot并广播新的配置信息。后续如果A后来又恢复了重新加入集群它会发现自己现有的config epoch比对方小于是作为新master的从节点存在重新同步数据。整体看下来一次干净利落的故障转移大概需要16~20秒左右。这个延迟主要是由cluster-node-timeout决定的。如果你觉得太慢可以调小这个参数比如10秒甚至5秒但要考虑网络抖动带来的误判风险。我一般建议线上先保持15秒等对网络质量有充分把握再考虑调小。4.4 网络分区场景客户端会被坑在哪里如果只是单个节点宕机上面的时间线足够清晰了。真正容易出问题的场景是网络分区集群中的一部分节点与另一部分节点断开了连接但它们各自其实都还活着。举个例子5个主节点的集群其中2个主节点和它们的从节点在同一个机架这个机架的网络交换机挂了导致这2个主节点与其他3个主节点断连。Cluster的故障判定机制依然会工作其他3个主节点联系不上这2个会把这2个标记为PFAIL最终打成FAIL这2个主节点自己也联系不上其他3个但它们这边的某个从节点可能升级成功重新自成一个小集群。问题在于网络分区期间两个分区内的节点都以为自己是合法集群的一部分。如果客户端连接的是少数派分区里的主节点可能仍然能正常写入但这段写入数据在分区恢复后会被覆盖或丢弃。这种事在任何一个分布式存储系统里都算得上经典难题Redis Cluster同样没法完全避免。我能给的实践建议是部署时尽量避免让主节点从节点都集中在同一个故障域里跨机架、跨可用区分散部署能显著降低这种极端情况的发生概率。5. 客户端视角MOVED、ASK与smart client的配合5.1 没有哈希环客户端怎么知道key在哪分布式系统里客户端要读一个key首先得知道它去哪台机器。早期的一致性哈希方案需要客户端内置哈希环逻辑每个客户端库都要实现一遍版本一多就乱。Redis Cluster则换了个思路客户端不需要自己算key对应的slot再找节点而是先随便连上一个节点由服务端告诉它key的归属。当你向集群中某个节点发送命令时节点会先计算CRC16(key) % 16384得到slot编号再查自己是不是负责这个slot。如果是自己处理如果不是返回一个MOVED错误错误信息里包含目标节点的IP和端口。这一点我非常喜欢服务端持有全量的slot路由表客户端不需要单独同步一份集群拓扑只做错误响应处理就够了。最简单的客户端就是redis-cli -c它的-c参数会开启集群模式自动跟随MOVED重定向去正确的节点重发命令。如果没加-c你会直接看到一堆MOVED 1234 192.168.1.5:6379的报错这不是集群坏了是客户端不支持集群协议。5.2 MOVED和ASK的分工一个是永久搬家一个是临时借调MOVED和ASK长得像实际含义完全不同。面试里经常考这个点。MOVED表示目标key已经确定不归当前节点管了当前节点返回你去另一个节点找吧。这个信息是永久性的客户端收到MOVED后应该更新自己缓存的路由表如果它是smart client后续同类slot的请求直接发到新节点。ASK出现在slot迁移的过渡期。迁移过程中目标slot的一部分key还在源节点老节点上一部分已经被搬到了目标节点新节点。当源节点收到一个key请求时它发现这个key属于一个正在迁移中的slot但那个key恰好已经被迁走了于是返回一个ASK错误指引客户端去目标节点。关键区别在于MOVED是以后永远都去新节点ASK是只有这一次请求去新节点之后你还是当它归老节点管。所以smart client收到ASK时不能更新路由表只能重发这一次请求。收到MOVED则一定要更新路由表否则每次都要多走一跳。Redis把这个写得很严谨理解了这个细节再去看redis-cli --cluster reshard过程中出现的日志就不慌了。5.3 hash tag把多个key绑在同一个slot里的唯一办法集群模式对单key操作没有影响但对多key操作有严格限制。MGET、事务、Lua脚本等一次涉及多个key的操作要求所有key必须落在同一个slot里。如果你同时操作foo和bar它们的slot大概率不同Redis会直接返回CROSSSLOT错误。这时候就要用hash tag。只要key里出现{}Redis就只对大括号内的内容做CRC16计算。比如{user:1001}.profile {user:1001}.cart {user:1001}.orders它们的{}里都是user:1001slot必然相同就能放进同一个事务、同一个Lua脚本或同一条MGET命令里。hash tag是用一部分散列均匀性换来了多key操作的可行性。使用时要注意别把粒度设得太粗比如所有key都是{all}:xxx那所有key都挤进同一个slot分片彻底失效集群退化成了单机。我见过有人图省事这么干最后热点全部压在一个节点上和没上集群一样。6. 集群不是银弹脑裂、数据丢失与一致性边界6.1 脑裂是怎么发生的cluster和sentinel都有此风险脑裂这个词听起来吓人其实就是分布式系统里多个节点同时认为自己是主的状态。在Redis Cluster里典型场景是网络分区旧主节点与它的从节点被隔开了但旧主节点自己还活着还能接收客户端请求。分区之后从节点因为联系不上主节点发起选举并成功提升为新主节点。与此同时旧主节点所在的少数派分区里客户端仍然能连上旧主节点继续写数据。此时集群里就同时存在两个master一个在少数派分区里继续写旧数据一个在多数派分区里接管slot并同步新数据。等网络分区恢复两个主节点重新建立连接集群会通过config epoch来做版本对账。原理不复杂谁的数据版本新谁说了算。多数派分区这边选举产生的master通常配置版本号更高旧主节点发现对方版本号比自己高就甘愿降级为从节点。但降级之后它要重新从新主节点全量同步数据这意味着分区期间旧主节点上新增的写入数据会被直接覆盖掉。6.2 导致数据丢失的两个关键窗口这段是面试最爱考的Redis集群在主从切换时数据丢失有两个天然窗口。第一个窗口是主节点异步复制带来的延迟窗口。Redis主从复制默认是异步的主节点收到写命令后先自己执行并返回给客户端再通过复制流把命令发给从节点。如果主节点刚好在已经回复客户端、还没把数据同步给从节点的时候宕机从节点升为主后自然没有这部分数据丢了就丢了。第二个窗口是脑裂导致的过期写窗口。刚才说的网络分区场景里旧主节点在分区期间继续接收写入这些写入在旧主节点降级后被全量同步覆盖同样全都丢了。所以Redis官方文档对Cluster一致性的描述非常坦诚Redis Cluster不保证强一致性在特定故障场景下会丢失写入。如果你对数据丢失零容忍就不应该把Redis当成唯一的数据源正确姿势是把它当缓存底层数据库保留最终数据或者对写请求做双写。设计系统时能接受多大的数据丢失决定了你该怎么用集群这是架构层面的一笔账。6.3 理解Redis Cluster的最终一致性和取舍用CAP理论来看Redis Cluster在发生网络分区时选择的是优先可用性它是AP系统分区发生时一边继续提供服务一边在后台努力收敛状态最后通过网络分区恢复后的对账达成最终一致。这个取舍对缓存场景非常合理如果请求打到RedisRedis因为分区拒绝服务对业务影响反而更大丢失一小段缓存数据顶多重查数据库回填。我自己的经验是不要拿Redis集群和数据库的强一致集群做类比。数据库挂了要立刻停写保护一致性Redis挂了要尽量继续提供缓存服务两者设计哲学就是反着的。想清楚这一点你就能理解为什么Cluster的故障转移并不追求零延迟为什么异步复制下从节点升级不检查数据是否绝对最新为什么网络分区后旧主数据会被覆盖。这套不完美的设计恰恰是在复杂网络环境里长期稳定运行的关键。7. 部署与调优中容易被忽略的细节7.1 最小6节点和实例规划的讲究网上总有人说Redis集群最少要6个节点这个说法背后是有逻辑的。集群要保证分片数据不丢失至少需要3个master每个master至少有1个从节点3加3就是6个。如果你只想要水平拆分、不关心高可用那3个master确实就够但只要想在master宕机后自动切换从节点就必不可少。实际生产规划时还要考虑几个因素每个master的内存不要超过物理内存的一半因为从节点全量同步时会fork子进程内存开销翻倍从节点不应该和它对应的主节点放在同一台物理机上否则机器挂了主从一起没如果追求更高可用性可以给某个热点master配2个从节点或者利用cluster-migration-barrier参数让集群自动从从节点冗余的master迁移一个从节点给没有任何从节点的master兜底。7.2 配置要点从cluster-enabled到cluster-require-full-coverage开启Redis集群之前先把配置项捋清楚。我用的是最常用的几个# 开启集群模式 cluster-enabled yes # 每个节点独立的集群状态文件千万不能共享 cluster-config-file nodes-6379.conf # 节点间通信超时时间默认15000毫秒 cluster-node-timeout 15000 # 从节点优先级数字越小越优先0表示不参与选举 replica-priority 100 # slot没有全部被覆盖时是否拒绝服务 cluster-require-full-coverage no重点说最后一个cluster-require-full-coverage。旧版本默认是yes意思是只要任何一个slot没有被任何master接管比如某个master挂掉了且它的从节点也没能成功切换整个集群就会拒绝所有读写请求。这个设计的初衷是防止读到不完整的数据但实测中代价太大一个节点出问题业务全挂。Redis 7.0之后默认改成了no也就是允许部分slot失效其他slot继续提供服务。建议生产环境显式配置成no并接受部分key暂时不可访问的代价至少别让整个集群停摆。另外再提一句cluster-config-file这个文件由Redis自己写记录节点ID、角色、slot分配等元数据。每个节点必须用不同的文件名否则多实例共享同一份文件会互相污染元数据。我踩过这个坑两个实例在同一个目录下启动互相把对方的信息覆盖掉最后只能重置集群。7.3 单机多实例、Docker和NAT场景下的端口坑搭建测试环境时很多人喜欢在一台机器上起多个Redis实例模拟集群。这时候要记住每个实例不仅有客户端端口还有集群总线端口。默认情况下总线端口是客户端端口加10000比如7001对应17001。如果启动了防火墙或者Docker做端口映射时只映射了客户端端口而忘了映射总线端口节点之间会一直握手失败cluster nodes里看到所有节点都是disconnected状态。在Docker或跨主机部署时还要额外配置公告地址参数。因为容器内部和宿主机外部的网络视角不同节点在Gossip消息里宣告的IP和端口必须是其他节点能够访问的。这时候要用cluster-announce-ip 10.0.0.5 cluster-announce-port 6379 cluster-announce-bus-port 16379我见过最典型的报错是容器正常启动cluster info显示集群状态正常但客户端从外部连接时老是被重定向到容器内网IP连不上。十有八九就是忘了配cluster-announce-ip。7.4 集群运维常用命令与故障定位思路最后分享几个我平时定位集群故障最高频的命令。先把命令记熟再遇到问题就有底气了# 查看集群节点状态包括角色、slot、连接状态 redis-cli -h 127.0.0.1 -p 6379 cluster nodes # 查看集群整体信息包括集群状态、slot分配数、节点数 redis-cli -h 127.0.0.1 -p 6379 cluster info # 查看slot与节点的对应关系 redis-cli -h 127.0.0.1 -p 6379 cluster slots # 重新分片迁移slot redis-cli --cluster reshard 127.0.0.1:6379 # 检查集群健康状态 redis-cli --cluster check 127.0.0.1:6379故障定位的核心思路我总结成一句口诀先看cluster info的cluster_state再看cluster nodes里有没有fail状态的节点最后用cluster slots确认路由是否完整。如果cluster_state:ok但某些key访问报错优先怀疑客户端是否开启集群模式如果cluster_state:fail大概率是某个slot没有master接管先看有没有节点处于fail或handshake状态如果一切看起来正常但性能上不去看看是不是热点key都挤在了同一个slot检查那个slot的访问量是否远高于其他slot。我在实际操作中还有一个体会升级Redis版本前一定要先在测试环境把集群完整跑一轮故障注入包括kill主节点、断网、重启从节点、缩容扩容确认版本的故障转移行为符合预期再上生产。这个习惯帮我避掉过好几次升级后偶发切换异常的坑。另外给从节点分配读流量前记得先发一条READONLY命令告诉节点我要在从库读否则就算连接到了从节点它也会按集群规则把写请求和未本slot的命令重定向走别到时候以为集群坏了其实只是协议没搞对。