Redis 3.x——集群运维

📅 2026/7/17 19:08:24
Redis 3.x——集群运维
集群运维1、集群完整性2、带宽消耗3、Pub/Sub广播问题4、集群倾斜4.1、数据倾斜4.2、请求倾斜5、集群读写分离5.1、只读连接5.2、读写分离6、手动故障转移7、数据迁移1、集群完整性为了保证集群完整性默认情况下当集群16384个槽任何一个没有指派到节点时整个集群不可用。执行任何键命令返回(error)CLUSTERDOWN Hash slot not served错误。这是对集群完整性的一种保护措施保证所有的槽都指派给在线的节点。但是当持有槽的主节点下线时从故障发现到自动完成转移期间整个集群是不可用状态对于大多数业务无法容忍这种情况因此建议将参数cluster-require-full-coverage配置为no当主节点故障时只影响它负责槽的相关命令执行不会影响其他主节点的可用性。2、带宽消耗集群内Gossip消息通信本身会消耗带宽官方建议集群最大规模在1000以内也是出于对消息通信成本的考虑因此单集群不适合部署超大规模的节点。在之前节点通信小节介绍到集群内所有节点通过ping/pong消息彼此交换信息节点间消息通信对带宽的消耗体现在以下几个方面消息发送频率跟cluster-node-timeout密切相关当节点发现与其他节点最后通信时间超过cluster-node-timeout/2时会直接发送ping消息。消息数据量每个消息主要的数据占用包含slots槽数组2KB空间和整个集群1/10的状态数据10个节点状态数据约1KB​。节点部署的机器规模机器带宽的上线是固定的因此相同规模的集群分布的机器越多每台机器划分的节点越均匀则集群内整体的可用带宽越高。例如一个总节点数为200的Redis集群部署在20台物理机上每台划分10个节点cluster-node-timeout采用默认15秒这时ping/pong消息占用带宽达到25Mb。如果把cluster-node-timeout设为20对带宽的消耗降低到15Mb以下。集群带宽消耗主要分为读写命令消耗Gossip消息消耗。因此搭建Redis集群时需要根据业务数据规模和消息通信成本做出合理规划在满足业务需要的情况下尽量避免大集群。同一个系统可以针对不同业务场景拆分使用多套集群。这样每个集群既满足伸缩性和故障转移要求还可以规避大规模集群的弊端。如笔者维护的一个推荐系统根据数据特征使用了5个Redis集群每个集群节点规模控制在100以内。适度提高cluster-node-timeout降低消息发送频率同时cluster-node-timeout还影响故障转移的速度因此需要根据自身业务场景兼顾二者的平衡。如果条件允许集群尽量均匀部署在更多机器上。避免集中部署如集群有60个节点集中部署在3台机器上每台部署20个节点这时机器带宽消耗将非常严重。3、Pub/Sub广播问题Redis在2.0版本提供了Pub/Sub发布/订阅功能用于针对频道实现消息的发布和订阅。但是在集群模式下内部实现对所有的publish命令都会向所有的节点进行广播造成每条publish数据都会在集群内所有节点传播一次加重带宽负担如图所示通过命令演示Pub/Sub广播问题如下所示1)对集群所有主从节点执行subscribe命令订阅cluster_pub_spread频道用于验证集群是否广播消息127.0.0.1:6379subscribe cluster_pub_spread127.0.0.1:6380subscribe cluster_pub_spread127.0.0.1:6382subscribe cluster_pub_spread127.0.0.1:6383subscribe cluster_pub_spread127.0.0.1:6385subscribe cluster_pub_spread127.0.0.1:6386subscribe cluster_pub_spread2)在6379节点上发布频道为cluster_pub_spread的消息127.0.0.1:6379publish cluster_pub_spread message_body_13)集群内所有的节点订阅客户端全部收到了消息127.0.0.1:6380subscribe cluster_pub_spread1)message2)cluster_pub_spread3)message_body_1 127.0.0.1:6382 subscribe cluster_pub_spread 1) message 2) cluster_pub_spread 3) message_body_1 …针对集群模式下publish广播问题需要引起开发人员注意当频繁应用Pub/Sub功能时应该避免在大量节点的集群内使用否则会严重消耗集群内网络带宽。针对这种情况建议使用sentinel结构专门用于Pub/Sub功能从而规避这一问题。4、集群倾斜集群倾斜指不同节点之间数据量和请求量出现明显差异这种情况将加大负载均衡和开发运维的难度。因此需要理解哪些原因会造成集群倾斜从而避免这一问题。4.1、数据倾斜数据倾斜主要分为以下几种节点和槽分配严重不均。不同槽对应键数量差异过大。集合对象包含大量元素。内存相关配置不一致。1)节点和槽分配严重不均。针对每个节点分配的槽不均的情况可以使用redis-trib.rb info{hostip}进行定位命令如下#redis-trib.rb info 127.0.0.1:6379127.0.0.1:6379(cfb28ef1…)-33348keys|5461slots|1slaves.127.0.0.1:6380(8e41673d…)-33391keys|5461slots|1slaves.127.0.0.1:6386(475528b1…)-33263keys|5462slots|1slaves.[OK]100002keysin3masters.6.10keys per slot on average.以上信息列举出每个节点负责的槽和键总量以及每个槽平均键数量。当节点对应槽数量不均匀时可以使用redis-trib.rb rebalance命令进行平衡#redis-trib.rb rebalance 127.0.0.1:6379…[OK]All16384slots covered. *** No rebalancing needed!All nodes are within the2.0% threshold.2)不同槽对应键数量差异过大。键通过CRC16哈希函数映射到槽上正常情况下槽内键数量会相对均匀。但当大量使用hash_tag时会产生不同的键映射到同一个槽的情况。特别是选择作为hash_tag的数据离散度较差时将加速槽内键数量倾斜情况。通过命令clustercountkeysinslot{slot}可以获取槽对应的键数量识别出哪些槽映射了过多的键。再通过命令clustergetkeysinslot{slot}{count}循环迭代出槽下所有的键。从而发现过度使用hash_tag的键。3)集合对象包含大量元素。对于大集合对象的识别可以使用redis-cli–bigkeys命令识别具体使用见12.5节。找出大集合之后可以根据业务场景进行拆分。同时集群槽数据迁移是对键执行migrate操作完成过大的键集合如 几百兆容易造成migrate命令超时导致数据迁移失败。4)内存相关配置不一致。内存相关配置指hash-max-ziplist-value、set-max-intset-entries等压缩数据结构配置。当集群大量使用hash、set等数据结构时如果内存压缩数据结构配置不一致极端情况下会相差数倍的内存从而造成节点内存量倾斜。4.2、请求倾斜集群内特定节点请求量/流量过大将导致节点之间负载不均影响集群均衡和运维成本。常出现在热点键场景当键命令消耗较低时如小对象的get、set、incr等即使请求量差异较大一般也不会产生负载严重不均。但是当热点键对应高算法复杂度的命令或者是大对象操作如hgetall、smembers等会导致对应节点负载过高的情况。避免方式如下合理设计键热点大集合对象做拆分或使用hmget替代hgetall避免整体读取。不要使用热键作为hash_tag避免映射到同一槽。对于一致性要求不高的场景客户端可使用本地缓存减少热键调用。5、集群读写分离5.1、只读连接集群模式下从节点不接受任何读写请求发送过来的键命令会重定向到负责槽的主节点上其中包括它的主节点​。当需要使用从节点分担主节点读压力时可以使用readonly命令打开客户端连接只读状态。之前的复制配置slave-read-only在集群模式下无效。当开启只读状态时从节点接收读命令处理流程变为如果对应的槽属于自己正在复制的主节点则直接执行读命令否则返回重定向信息。命令如下// 默认连接状态为普通客户端:flagsN127.0.0.1:6382client listid3addr127.0.0.1:56499fd6nameage130idle0flagsNdb0sub0psub0multi-1qbuf0qbuf-free32768obl0oll0omem0eventsrcmdclient // 命令重定向到主节点127.0.0.1:6382get key:test:3130(error)MOVED12944127.0.0.1:6379 // 打开当前连接只读状态127.0.0.1:6382readonlyOK // 客户端状态变为只读:flagsr127.0.0.1:6382client listid3addr127.0.0.1:56499fd6nameage154idle0flagsrdb0sub0psub0multi-1qbuf0qbuf-free32768obl0oll0omem0eventsrcmdclient // 从节点响应读命令127.0.0.1:6382get key:test:3130value:3130readonly命令是连接级别生效因此每次新建连接时都需要执行readonly开启只读状态。执行readwrite命令可以关闭连接只读状态。5.2、读写分离集群模式下的读写分离同样会遇到复制延迟读取过期数据从节点故障等问题具体细节见6.5复制运维小节。针对从节点故障问题客户端需要维护可用节点列表集群提供了cluster slaves{nodeId}命令返回nodeId对应主节点下所有从节点信息数据格式同cluster nodes命令如下// 返回6379节点下所有从节点127.0.0.1:6382cluster slaves cfb28ef1deee4e0fa78da86abe5d24566744411e1)40622f9e7adc8ebd77fca0de9edfe691cb8a74fb 127.0.0.1:6382 myself,slave cfb28e f1deee4e0fa78da86abe5d24566744411e 0 0 3 connected2)2e7cf7539d076a1217a408bb897727e5349bcfcf 127.0.0.1:6384 slave,fail cfb28ef1 deee4e0fa78da86abe5d24566744411e 1473047627396 1473047622557 13 disconnected解析以上从节点列表信息排除fail状态节点这样客户端对从节点的故障判定可以委托给集群处理简化维护可用从节点列表难度。集群模式下读写分离涉及对客户端修改如下维护每个主节点可用从节点列表。针对读命令维护请求节点路由。从节点新建连接开启readonly状态。集群模式下读写分离成本比较高可以直接扩展主节点数量提高集群性能一般不建议集群模式下做读写分离。集群读写分离有时用于特殊业务场景如利用复制的最终一致性使用多个从节点做跨机房部署降低读命令网络延迟。主节点故障转移时间过长业务端把读请求路由给从节点保证读操作可用。以上场景也可以在不同机房独立部署Redis集群解决通过客户端多写来维护读命令直接请求到最近机房的Redis集群或者当一个集群节点故障时客户端转向另一个集群。6、手动故障转移Redis集群提供了手动故障转移功能指定从节点发起转移流程主从节点角色进行切换从节点变为新的主节点对外提供服务旧的主节点变为它的从节点如图所示。在从节点上执行cluster failover命令发起转移流程默认情况下转移期间客户端请求会有短暂的阻塞但不会丢失数据流程如下从节点通知主节点停止处理所有客户端请求。主节点发送对应从节点延迟复制的数据。从节点接收处理复制延迟的数据直到主从复制偏移量一致为止保证复制数据不丢失。从节点立刻发起投票选举这里不需要延迟触发选举​。选举成功后断开复制变为新的主节点之后向集群广播主节点pong消息故障转移细节见10.6故障恢复部分。旧主节点接受到消息后更新自身配置变为从节点解除所有客户端请求阻塞这些请求会被重定向到新主节点上执行。旧主节点变为从节点后向新的主节点发起全量复制流程。主从节点转移后新的从节点由于之前没有缓存主节点信息无法使用部分复制功能所以会发起全量复制当节点包含大量数据时会严重消耗CPU和网络资源线上不要频繁操作。Redis4.0的Psync2将有效改善这一问题。手动故障转移的应用场景主要如下1)主节点迁移运维Redis集群过程中经常遇到调整节点部署的问题如节点所在的老机器替换到新机器等。由于从节点默认不响应请求可以安全下线关闭但直接下线主节点会导致故障自动转移期间主节点无法对外提供服务影响线上业务的稳定性。这时可以使用手动故障转移把要下线的主节点安全的替换为从节点后再做下线操作操作如图所示。2)强制故障转移。当自动故障转移失败时只要故障的主节点有存活的从节点就可以通过手动转移故障强制让从节点替换故障的主节点保证集群的可用性。自动故障转移失败的场景有主节点和它的所有从节点同时故障。这个问题需要通过调整节点机器部署拓扑做规避保证主从节点不在同一机器/机架上。除非机房内大面积故障否则两台机器/机架同时故障概率很低。所有从节点与主节点复制断线时间超过cluster-slave-validity-factor*cluster-node-tineoutrepl-ping-slave-period导致从节点被判定为没有故障转移资格手动故障转移从节点不做中断超时检查。由于网络不稳定等问题故障发现或故障选举时间无法在cluster-node-timeout*2内完成流程会不断重试最终从节点复制中断时间超时失去故障转移资格无法完成转移。集群内超过一半以上的主节点同时故障。根据以上情况cluster failover命令提供了两个参数force/takeover提供支持cluster failover force——用于当主节点宕机且无法自动完成故障转移情况。从节点接到cluster failover force请求时从节点直接发起选举不再跟主节点确认复制偏移量从节点复制延迟的数据会丢失​当从节点选举成功后替换为新的主节点并广播集群配置。cluster failover takeover——用于集群内超过一半以上主节点故障的场景因为从节点无法收到半数以上主节点投票所以无法完成选举过程。可以执行clusterfailover takeover强制转移接到命令的从节点不再进行选举流程而是直接更新本地配置纪元并替换主节点。takeover故障转移由于没有通过领导者选举发起故障转移会导致配置纪元存在冲突的可能。当冲突发生时集群会以nodeId字典序更大的一方配置为准。因此要小心集群分区后手动执行takeover导致的集群冲突问题。如图所示。图中Redis集群分别部署在2个同城机房机房A部署节点master-1、master-2、master-3、slave-4。机房B部署节点slave-1、slave-2、slave-3、master-4。当机房之间出现网络中断时机房A内的节点持有半数以上主节点可以完成故障转移会将slave-4转换为master-4。如果客户端应用都部署在机房B运维人员为了快速恢复对机房B的Redis访问对slave-1slave-2slave-3分别执行cluster failover takeover强制故障转移让机房B的节点可以快速恢复服务。当机房专线恢复后Redis集群会拥有两套持有相同槽信息的主节点。这时集群会使用配置纪元更大的主节点槽信息配置纪元相等时使用nodeId更大的一方因此最终会以哪个主节点为准是不确定的。如果集群以机房A的主节点槽信息为准则这段时间内对机房B的写入数据将会丢失。综上所述在集群可以自动完成故障转移的情况下不要使用cluster failover takeover强制干扰集群选举机制该操作主要用于半数以上主节点故障时采取的强制措施请慎用。手动故障转移时在满足当前需求的情况下建议优先级cluster failvercluster failover forcecluster failover takeover。7、数据迁移应用Redis集群时常需要把单机Redis数据迁移到集群环境。redis-trib.rb工具提供了导入功能用于数据从单机向集群环境迁移的场景命令如下redis-trib.rbimporthost:port--fromarg--copy--replaceredis-trib.rb import命令内部采用批量scan和migrate的方式迁移数据。这种迁移方式存在以下缺点迁移只能从单机节点向集群环境导入数据。不支持在线迁移数据迁移数据时应用方必须停写无法平滑迁移数据。迁移过程中途如果出现超时等错误不支持断点续传只能重新全量导入。使用单线程进行数据迁移大数据量迁移速度过慢。正因为这些问题社区开源了很多迁移工具这里推荐一款唯品会开发的redis-migrate-tool该工具可满足大多数Redis迁移需求特点如下支持单机、Twemproxy、Redis Cluster、RDB/AOF等多种类型的数据迁移。工具模拟成从节点基于复制流迁移数据从而支持在线迁移数据业务方不需要停写。采用多线程加速数据迁移过程且提供数据校验和查看迁移状态等功能。更多细节见GitHubhttps://github.com/vipshop/redis-migrate-tool。