当前位置:首页 > 问答 > 正文

Redis 集群那些坑和麻烦,咋整才能少踩雷避免崩盘

说到Redis集群,它确实是个好东西,数据一分摊,性能和高可用性都上来了,但俗话说得好,“好东西都有脾气”,Redis集群这玩意儿要是没摸透它的性子,踩起坑来那可是能让运维同学半夜从床上蹦起来的,下面我就结合网上不少老司机的血泪史,比如像知乎上一些技术分享、官方文档的警告以及一些公司的故障复盘报告,聊聊那些常见的坑和避雷指南。

第一大坑:网络闪断要人命,集群直接闹分家

Redis集群对网络环境的要求非常高,它不像单机或者简单的主从,挂了一个从库还能顶一下,集群里的节点之间是靠“心跳”来维持联系的,默认每隔一秒通个信,如果超过一定时间(默认15秒)没收到某个节点的消息,其他节点就认为这个节点“死”了,然后就开始开会(投票),把它标记为故障节点。

问题就出在这儿,万一你的服务器网络不太稳定,偶尔抽风一下,闪断个两三秒又恢复了,但这点时间可能已经足够让集群做出误判了,结果就是,一个明明健康的节点被踢出了集群,这会导致一系列连锁反应:数据槽(slot)要重新分配,客户端可能会收到一堆MOVED或者ASK错误,请求被转来转去,服务瞬间就不稳定了,更糟的是,如果被误踢的是个主节点,集群还会触发故障转移,选个从库上来当主库,这一通操作本身就有风险,而且等那个被误踢的主节点网络恢复了,它回来的时候数据可能已经落后了,还得做数据同步,又是一番折腾。

咋整? 核心思路就是给网络“维稳”。确保你的集群所有节点都在同一个局域网(IDC内部)里,并且网络质量有保障,别跨机房部署除非你用专线而且确保延迟极低,可以适当调整集群的配置参数,比如把判断节点失效的超时时间 cluster-node-timeout 调大一点,比如调到20秒或30秒,给网络波动留点余地,但也不能调太大,否则真故障了反应又太慢,需要根据实际情况权衡。

第二大坑:扩容缩容不谨慎,数据迁移变灾难

业务量增长了,加几个节点扩容;业务收缩了,下线几个节点缩容,这很正常,但Redis集群的扩容缩容可不是点几下鼠标就完事儿的,它涉及到数据槽的重新分配和数据迁移。

这里面的麻烦在于数据迁移是异步的,当你把一部分数据槽从一个节点迁移到另一个节点时,源节点会开始把数据一点点地发给目标节点,在这个过程中,客户端来访问这些正在迁移的数据,源节点会处理一部分,如果发现数据已经迁走了,就会给客户端返回一个ASK错误,告诉它“去那个新节点找”,客户端得能正确处理这个ASK转向才行。

但如果操作太猛,比如一次性迁移太多的数据槽,或者迁移过程中源节点和目标节点的负载本来就很高,就可能出问题,比如迁移卡住了,或者网络带宽被占满,影响正常服务,更可怕的是在迁移没完成的时候,贸然把源节点给下线了,那部分还在迁移路上的数据可就丢了,找都找不回来。

咋整? 扩容缩容一定要“慢工出细活”,第一,选择业务低峰期操作,第二,分批迁移,一次只迁移少量几个数据槽,观察一下集群和节点的状态(CPU、内存、网络IO),稳定了再继续下一批,第三,用好监控工具,紧盯数据迁移的进度和延迟,确保迁移完全完成了再进行下一步操作,官方工具 redis-cli --cluster reshard 会给你提示,一定要仔细看,别一路回车按下去。

第三大坑:键值太大或太多,单个节点被压垮

Redis集群是把数据分散到16384个槽里,再分配到不同节点上,但它分散的依据是对key进行CRC16校验后再取模,这意味着,一个很大的key(比如一个包含几百万元素的超大Hash或List)它只会存在于某一个节点上,不会被拆分。

这就埋下了隐患,如果你的应用设计不当,产生了所谓的“大key”,比如把一个文章的所有评论都塞进一个List里,那么这个key所在的节点内存占用会特别高,访问这个key的网络流量也会集中打在这个节点上,很容易导致这个节点内存溢出(OOM)或者带宽打满,从而拖累整个集群的性能,甚至导致该节点宕机,同样,如果某个节点上的key数量特别多,即使每个key不大,在持久化(RDB或AOF)时也可能引发问题。

咋整? 这要求我们在设计应用时就要有集群意识,第一,避免产生大key,大的数据结构要想办法拆分,比如可以把一个大的Hash拆成多个小的Hash,通过key的命名规则来关联,第二,对key进行均匀分布的命名,可以使用标签(tag)或者散列值,让数据能更均匀地分布到不同节点上,避免热点问题,第三,建立监控预警,对每个节点的内存使用量、key数量、网络流量进行监控,发现异常增长及时告警处理。

第四大坑:客户端太“傻”,不懂集群规矩

你的Redis集群配置得再完美,如果用的客户端库(比如Java的Jedis,Python的redis-py等)不支持集群模式,或者配置不对,那也白搭,一个“聪明”的集群客户端,应该在启动时就从集群节点获取一份“路由表”(哪个槽在哪个节点上),然后直接向正确的节点发送命令。

但“傻”客户端可能只会连接你给的一个节点地址,然后所有请求都发过去,这个节点还得帮你转发请求到正确的节点,增加了延迟和负担,或者客户端没有正确解析和处理集群返回的MOVED、ASK错误,不会自动更新路由表,导致请求一直失败。

咋整? 务必使用官方推荐或经过验证的、支持Redis集群协议的客户端库,并正确配置集群的所有节点地址(或者至少配置多个,让它能自动发现),这样客户端才能智能地路由请求,并在集群拓扑发生变化时(如故障转移、扩容)自动更新连接。

总结一下

Redis集群不是装了就能高枕无忧的银弹,要想少踩雷,关键在于:保障稳定的网络环境、像对待手术一样谨慎地进行扩缩容、从应用设计源头避免大key和热点、以及选用靠谱且正确配置的客户端,再加上完善的监控告警(节点状态、慢查询、内存、网络),才能把这个威力强大的工具用好,让它真正为业务保驾护航,而不是变成一个随时可能引爆的炸弹。

Redis 集群那些坑和麻烦,咋整才能少踩雷避免崩盘