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

红色锁卡住的不光是时间,还有那些隐藏的redis等待问题

(根据微信公众号“技术琐话”一篇题为《除了超时,你还需要关注这些Redis隐藏问题》的文章整理)

我们平时在使用Redis的时候,最常碰到的、也是最容易警觉的问题,大概就是“超时”了,在代码里,我们通常会设置一个连接Redis或者操作Redis的超时时间,比如500毫秒,一旦超过这个时间还没得到响应,程序就会抛出一个超时异常,然后我们可能会记录一条错误日志,或者触发降级处理,表面上看,问题很简单:Redis慢了,或者网络卡了,很多人解决问题的思路就停留在:是不是Redis服务器内存满了?是不是某个操作太耗时?或者干脆把超时时间从500毫秒调大到1秒。

这种思路,就像只看到了冰山露出水面的一角,那个超时异常,那个红色的报错日志,就像一把锁,它卡住的不仅仅是那几百毫秒的程序执行时间,更关键的是,它锁住了我们的视线,让我们忽略了水面之下那些更复杂、更隐蔽的“等待”状态,这些状态,往往才是导致系统出现诡异问题的真正元凶。

其中一个非常典型的隐藏问题,就是连接池耗尽导致的等待,想象一下,你的应用服务背后有一个连接Redis的连接池,这个池子的大小是固定的,比如20个连接,当业务高峰来临,并发请求增多,这20个连接很快就被占满了,这时,第21个请求过来,它不会立刻失败,而是会进入一个“等待队列”,它会眼巴巴地等着,希望有哪个用完的连接能被释放回池子里,自己好顶上去,这个等待行为,在很多的连接池默认配置下,是没有时间限制的,或者等待时间设置得非常长。

可怕的事情就可能发生了,如果Redis服务器真的因为某些原因(比如执行了一个特别慢的keys *操作)变得响应极其缓慢,那么当前持有连接的20个请求都会被拖住,迟迟无法完成,这就导致连接池里的20个连接谁也释放不了,后面排队等待的那些请求,等的就不是几百毫秒了,它们可能会等待数秒甚至数十秒,直到最前面那个慢操作完成,对于用户来说,感受就是页面“卡死”了,一直转圈,最后可能才统一报错,而你在监控上,可能只会看到最初那20个请求的超时日志,后面大量请求的漫长等待被完全掩盖了,问题根因很难被迅速定位。

另一个隐藏更深的问题,是操作系统TCP层面的“慢”或“丢包”,我们程序里设置的超时,通常是应用层的超时,但一个请求从发出到收到响应,需要经过复杂的网络链路,如果某个网络节点出现轻微丢包,TCP协议为了保证可靠性,会主动进行重传,这个重传的过程对应用程序来说是透明的,但它会实实在在地消耗时间,可能应用层觉得刚发出请求,还在等响应,而底层TCP正在一遍又一遍地尝试重传一个丢失的数据包,这种情况下,应用层的超时可能最终会触发,但错误信息依然是笼统的“超时”,我们很难意识到这背后是网络基础设施的问题,而非Redis服务本身的问题。

还有一种情况是Redis服务器的“假死”,它可能因为持久化操作(如生成RDB快照)导致所有读写操作被阻塞,这时,服务器虽然进程还在,但已经无法响应任何请求,客户端发出的请求同样会陷入漫长的等待,这种等待和简单的性能下降不同,它是完全的停滞,如果不结合Redis服务器的监控指标(如持久化日志、内存和CPU使用情况),仅从客户端日志看,依然只是一片超时警告,无法判断Redis内部到底发生了什么。

当我们看到“红色锁”——那个超时异常时,不能简单地认为只是时间被卡住了,它更像是一个警报,提醒我们系统中有某个环节出现了瓶颈,我们需要顺着这个线索,去检查连接池的使用情况,看看是否有大量线程在等待获取连接;去监控网络状况,分析是否有丢包或重传;去观察服务器端的资源和使用情况,确认是否存在内部阻塞,只有拨开“超时”这层迷雾,看到其后各种形态的“等待”,我们才能真正地解锁问题,让系统恢复流畅,否则,就只是在问题的表面反复涂抹,无法根除隐患。

红色锁卡住的不光是时间,还有那些隐藏的redis等待问题