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

Redis某节点突然连不上了,排查原因和解决办法分享

那天下午,系统突然报警,说有一个Redis节点连不上了,应用那边报了一堆连接超时的错误,我们赶紧放下手里的活儿,开始排查,整个过程就像破案一样,一层层往下挖,下面我把当时排查的思路和最后找到的解决办法分享一下,这些经验很多来自我们团队的内部总结,也参考了Redis官方文档里的一些建议,还有网上一些老师傅的实战分享。

第一步,先确认是不是真的“连不上”,我们第一时间登录到那台出问题的服务器上,直接用redis-cli这个命令行工具去连本地的Redis端口,结果发现连是能连上,但特别慢,而且执行一个简单的ping命令都要等好几秒才返回,这说明服务还在,但是处于一个非常不健康的状态。“连不上”有时候是彻底断开,有时候是响应极慢导致超时,感觉像连不上。

第二步,我们马上看了服务器的基本情况,用top命令一看,CPU占用并不高,但是内存使用快满了,这时候我们怀疑是内存不够用,Redis是个内存数据库,如果物理内存快用完了,操作系统可能会开始用硬盘做交换分区,速度会慢得吓人,这就是为什么响应那么慢的原因,我们赶紧用info memory命令看了下Redis自己的内存统计,发现used_memory确实已经接近我们在配置文件里设置的maxmemory上限了。maxmemory_policy(内存满了的淘汰策略)设置的是noeviction,这个策略的意思是,当内存用满时,新写入的数据会报错,但不会淘汰老数据,这很可能导致部分写操作失败,并且整个实例压力巨大,这是第一个关键发现:内存用满,且淘汰策略太严格

但为什么内存突然就满了呢?这个节点之前运行挺稳定的,我们接着查,我们注意到,在出问题的时间点附近,有业务部门上线了一个新功能,这个功能会向这个Redis实例大量写入一种新的缓存数据,而且没有设置过期时间,这就像往一个已经快满的水池里又猛灌水,一下子就溢出了,原因指向了业务代码的异常写入,产生了大量“永生”数据

Redis某节点突然连不上了,排查原因和解决办法分享

第三步,检查网络和连接,虽然第一步在本机连接也很慢,但我们还是检查了网络,用netstat看了下连接数,发现连接数异常地高,而且有很多连接处于CLOSE_WAIT状态,这说明有很多客户端连接没有正常关闭,这可能是应用端连接池配置有问题,或者是在Redis响应变慢后,客户端不断重试创建新连接导致的雪球效应,我们参考了网上一些关于Redis连接优化的文章,提到连接池配置不当或客户端未正常释放连接会导致此类问题。

第四步,看Redis日志,这是最重要的线索来源之一,我们打开了Redis的日志文件(配置文件中logfile指定的路径),在出问题的时间点附近,看到了大量重复的警告信息,Can‘t save in background: fork: Cannot allocate memory”,这个错误非常关键!它说明Redis在尝试做持久化(比如RDB快照)时,无法分配更多内存,这是因为Redis做持久化时需要fork一个子进程,而fork操作虽然用写时复制技术,但在内存数据很大时,仍然需要消耗一部分内存,如果系统本身内存就紧张,fork就会失败,这解释了为什么在内存压力下,持久化操作会加剧问题,甚至导致Redis完全卡死,这个知识点在Redis官方文档关于持久化的章节里有详细说明。

Redis某节点突然连不上了,排查原因和解决办法分享

第五步,检查持久化配置,我们看了下配置文件,发现同时开启了RDB和AOF两种持久化方式,AOF文件已经长得非常大,重写(rewrite)可能也在进行中,这同样会消耗资源,在内存不足的情况下,多个后台进程(RDB的fork、AOF重写)一起抢资源,无疑是雪上加霜。

综合以上的排查,我们理清了问题链条:

  1. 直接诱因:业务代码突然写入大量无过期时间的数据,导致Redis内存使用迅速达到上限。
  2. 配置加剧maxmemory-policy设置为noeviction,内存满后拒绝写入,但未淘汰数据,服务进入半僵死状态。
  3. 资源枯竭:系统内存不足,导致Redis的fork操作(用于持久化)失败,持久化进程崩溃,进一步影响主进程稳定性。
  4. 连锁反应:Redis响应变慢,导致客户端连接堆积、超时、重试,形成恶性循环,最终从应用角度看就是“节点连不上了”。

解决办法我们分几步走:

  1. 紧急恢复:我们首先在命令行下,通过config set命令,临时将maxmemory-policynoeviction改成了allkeys-lru,这个命令是Redis官方文档里支持的动态调整配置,改了之后,Redis立刻开始淘汰最近最少使用的键,内存压力迅速下降,服务响应慢慢恢复,这是一个临时救命措施
  2. 清理数据:服务可连接后,我们立刻分析并删除了那批新写入的无用缓存数据,使用scan命令慢慢遍历,避免使用keys *造成阻塞。
  3. 调整配置:在恢复后,我们评审了Redis的配置文件,做了几处永久性修改:一是根据实际内存情况,适当调高了maxmemory限制(但不超过物理内存);二是将maxmemory-policy根据业务场景调整为更合适的volatile-lru(只淘汰设置了过期时间的键中的最近最少使用键);三是检查并优化了持久化策略,在内存紧张的实例上,权衡后暂时关闭了AOF,只使用RDB定时备份,以减轻fork压力。
  4. 业务侧整改:与开发团队沟通,强制规定所有缓存数据必须设置合理的过期时间,并对新上线功能进行缓存用量评估。
  5. 监控加强:在监控系统里增加了对Redis内存使用率、连接数、fork失败次数的告警,做到提前预警,而不是等连不上了才发现。

这次故障给我们的教训是,Redis连不上,往往不是简单的网络问题,而是资源(特别是内存)耗尽、配置不当、业务突增等多方面因素叠加的结果,排查时要像剥洋葱一样,从现象(连接慢/超时)入手,先看资源(内存、CPU),再看配置(内存策略、持久化),然后查日志(找错误关键词),同时要了解业务变更,才能最快定位到根本原因,临时调整配置可以快速止血,但长期一定要从配置优化、容量规划和业务规范上解决问题。