先讲个不算冷的知识点:生产环境里Redis请求超时,真正被定位到“Redis自身性能问题”的,其实不到一半。我见过太多团队把timeout参数从3秒调到5秒、再调到10秒,最后发现是网络链路在中间某个节点悄悄丢包的案例。
丢包这事的迷惑性在于,它不会让服务完全不可用,而是让你在最想骂人的时候来一下——偶尔一次GET卡了2秒,下一分钟又秒回,日志里报一句Read timed out,等你连上去看的时候Redis一切正常,CPU、内存、慢查询全都没问题。于是问题就被归咎于“Redis不稳定”,甚至被甩锅给“业务高峰期抖动”。
今天这篇就专门讲怎么把“疑似Redis超时”一步步拆解到“确实是网络丢包”,以及确认之后应该怎么处理。整个排查思路不只适用于Redis,对MySQL、MQ、任何走TCP协议的服务都通用。你只要把本文的方法吃透,下次再遇到“偶发超时”就能少走一大半弯路。
1. 超时现象分类与初步定位思路
1.1 三种超时类型的区分
Redis请求超时不是一个模糊的“卡了”,在客户端日志里它其实分得很清楚,只是很多人没细看。
- 连接超时(Connection timeout):客户端根本没能和Redis建立TCP连接,通常表现为
connect timed out。这类问题往往指向网络不可达、防火墙拦截、连接数耗尽或Redis本身accept队列满。 - 读取超时(Read timeout):连接建立成功,请求也发出去了,但在规定时间内没等来响应。HTTP里有
Read timed out,Jedis里对应JedisConnectionException: Unexpected end of stream或socket read timeout。这是网络丢包判断的重灾区。 - 写入超时(Write timeout):客户端向Redis发送命令时阻塞,通常是发送缓冲区满或对端接收能力下降。MySQL、Kafka等也常见这种,Redis下相对少一些,但一旦出现,基本就是大问题。
从运维和开发角度看,读取超时最值得警惕。因为连接能建立,说明网络“大体通”,但响应迟迟不来,说明数据包在链路某个环节丢了或被延迟处理了。很多人在第一步就没分清类型,直接把timeout调大,问题只会被掩盖,不会消失。
1.2 先别急着上工具,做三个快速判断
接到超时告警,我的习惯是先花三分钟回答三个问题,这三个答案决定后续排查方向。
第一,超时是普遍性的还是偶发性的?所有实例都超时,还是只有某一台业务服务器连某个Redis节点超时?前者大概率是Redis节点或网络骨干出问题,后者大概率是单台机器网卡、路由或连接池问题。
第二,超时集中在什么时间点?如果总是出现在业务高峰、定时任务执行时段、大key删除之后,那怀疑点完全不同。高峰期的超时往往伴随带宽打满或CPU冲击。
第三,Redis自身指标正不正常?在Redis上执行INFO,重点看connected_clients、instantaneous_ops_per_sec、used_memory、rejected_connections这几个值。如果客户端都连不上Redis,但它自身指标一切正常,那问题就基本锁定在网络链路上。
注意:执行
INFO本身也可能卡住。如果连INFO都无响应,说明问题已经很严重了,这时优先检查物理链路和交换机端口,而不是在Redis层面找原因。
1.3 日志时间戳里的门道
排查超时,日志时间戳一定要精确到毫秒级。很多业务系统默认时间戳只到秒,在偶发超时场景下,秒级精度根本看不出问题——同一个秒内请求可能成功了999次,失败了1次,你要能区分出失败请求和成功请求在时间上的差异。
我在实际项目中会做这样一个预处理:把客户端报超时的时间和Redis服务端的INFO里的uptime_in_seconds对齐,然后查看该时间点Redis有没有慢查询、有没有RDB持久化导致的分叉、有没有evicted_keys暴涨。这一步能快速过滤掉Redis服务端自身导致的超时。
如果Redis侧干净,客户端侧也排除了应用线程池阻塞,下一步就该系统性检查网络链路了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 网络链路诊断:从ping到tcpdump的完整工具链
2.1 ping并不是简单地“能通就行”
很多人用ping检查网络,看到能通就走了。但在排查丢包场景下,ping的输出信息量很大,不能只看通不通。
bash复制ping -i 0.2 -c 1000 10.0.0.10
-i 0.2表示每0.2秒发一个包,-c 1000表示发包1000个,这样能在合理时间内测出丢包率。不加参数的直接ping默认每秒一个包,测200秒才200个包,对偶发丢包几乎没什么参考价值。
重点看三列数据:
loss百分比:如果连续1000个包中出现哪怕1%的丢包,对Redis这种低延迟要求的服务来说都可能是致命的。time的波动幅度:time=0.3ms和time=50ms混在一起,即使没有丢包,也说明链路存在拥塞或队列延迟。min/avg/max三个值之间的差距:max和avg之间差几十倍,说明链路上有突发拥塞点。
不过有个坑必须提醒:ping响应是ICMP协议,有的路由器和交换机对ICMP做了限速或优先级调低,导致ping丢包但业务TCP流量本身就正常。反过来也有TCP丢包但ping完全正常的。所以ping只能作为“初步线索”,不能作为“最终结论”。
2.2 mtr:一条命令看清每一跳的情况
ping只是测试端到端连通性,无法告诉你丢包发生在哪一跳。mtr(My TraceRoute)结合了ping和tracert的功能,会持续探测经过的每一跳,并显示每跳的丢包率和延迟。
Linux下直接装:
bash复制apt-get install mtr-tiny
# 或
yum install mtr
使用方式:
bash复制mtr -rwc 100 10.0.0.10
-r表示report模式,-w用宽格式输出,-c 100发100个包。
输出示例:
code复制HOST: app-server Loss% Snt Last Avg Best Wrst StDev
1. 192.168.1.1 0.0% 100 0.2 0.3 0.2 0.5 0.0
2. 10.10.10.1 0.0% 100 0.5 0.6 0.4 1.2 0.1
3. 172.16.1.254 5.0% 100 2.1 2.5 1.8 5.6 0.6
4. 10.0.0.10 4.0% 100 1.9 2.3 1.7 4.8 0.5
判断标准:如果倒数第二跳(即到达Redis服务器之前的最后一跳)有丢包,但最后一跳(Redis服务器)没有丢包,那丢包点基本在倒数第二跳设备上。如果最后一跳丢包,可能是服务器本身网卡问题。
mtr有个经典的“谎报丢包”现象:某些路由器在处理ICMP时用软件转发,压力大时会丢弃探测包,但实际数据报文走的是硬件转发,完全不受影响。所以mtr结果要结合TCP层的真实抓包判断,不能看到Loss 5%就下结论。
2.3 tcpdump抓包:最接近真相的手段
诊断网络丢包,最权威的依据不是ping也不是mtr,而是直接抓包看TCP重传。TCP协议本身有重传机制,数据包丢了会触发重传,而重传间隔是指数退避的。 你在抓包里看到这个规律,就能确认丢包真实存在。
在Redis客户端所在服务器上抓包:
bash复制tcpdump -i eth0 host 10.0.0.10 and port 6379 -w /tmp/redis_dump.pcap
在Redis服务器上也同步抓一份:
bash复制tcpdump -i eth0 host 10.0.0.20 and port 6379 -w /tmp/redis_server_dump.pcap
两边同时抓,是为了判断丢包方向。如果客户端发出的包到了Redis,但Redis回包客户端没收到,说明是客户端到Redis的“响应路径”丢包;反过来就是“请求路径”丢包。
抓完之后用tcpdump -r进行分析,或者导出到Wireshark里看。你重点要找的,是连续出现的TCP Retransmission包。一个典型的丢包序列长这样:
code复制12:00:00.001 IP client.50000 > redis.6379: Flags [P.], length 30
12:00:00.003 IP client.50000 > redis.6379: Flags [P.], length 30 (TCP Retransmission)
12:00:00.007 IP client.50000 > redis.6379: Flags [P.], length 30 (TCP Retransmission)
12:00:00.015 IP client.50000 > redis.6379: Flags [P.], length 30 (TCP Retransmission)
注意看重传间隔:第1次和第2次之间隔了2ms,第2次和第3次之间隔了4ms,第3次和第4次之间隔了8ms——这种“翻倍增长”的重传间隔,是TCP指数退避的经典特征,也是确认网络丢包的铁证。
注意:看到重传不一定是“物理丢包”,也可能是网络拥塞导致的延迟过大触发了RTO超时。但不管哪种,都说明TCP层出现了异常,都需要继续追查到底。
2.4 ss和netstat:看TCP统计与重传计数
在动tcpdump之前,先用系统自带的TCP统计信息做快速判断,效率更高。
bash复制netstat -s | grep -i retransmit
ss -s
netstat -s输出里有一个TCPLostRetransmit和TCPRetransFail,数值增速如果很快,说明丢包重传确实在发生。ss -s能看到当前TCP连接状态分布,如果TIME-WAIT、SYN-SENT异常多,也是网络质量差的信号。
另外我还常用一个指标:
bash复制cat /proc/net/netstat | grep -A1 TcpExt
看TCPLostRetransmit、ListenOverflows、TCPBacklogDrop等计数。尤其ListenOverflows如果持续增长,说明Redis的accept队列不够用,连接建立阶段就已经出了问题。
3. Redis侧排查:先证明“不是我的锅”
3.1 Redis配置项里的关键参数
排查到网络之前,必须先排除Redis自己的问题。所谓“自己”,不只是CPU和内存,还包括几个TCP相关的配置项。
timeout参数表示空闲连接超时时间,如果客户端使用了连接池但长期空闲,Redis可能主动断开连接。查看方式:
bash复制redis-cli CONFIG GET timeout
# 默认通常是0,表示不超时
tcp-keepalive参数控制TCP keepalive包的发送间隔,默认300秒。如果客户端和服务端之间隔了防火墙,防火墙会在一定时间后清理空闲连接,导致连接”假死“。如果你的超时日志里总是出现在连接空闲一段时间后的第一次请求,优先怀疑这个。
bash复制redis-cli CONFIG GET tcp-keepalive
还有maxclients,Redis默认最大连接数10000。连接数满了之后新连接会被拒绝,表现为“连接超时”或“连接被重置”。这不算网络丢包,但报错现象极其相似,排查时不可漏掉。
3.2 用Redis自带命令做快速体检
Redis提供了几个内置命令,非常适合在超时现场做“快照式”体检。
INFO stats:看总连接数、总命令数、rejected_connections、evicted_keys。INFO commandstats:看每个命令的调用次数和耗时分布,能定位是否有异常慢的命令。SLOWLOG GET 50:看最近50条慢查询。慢查询超过slowlog-log-slower-than阈值(默认10000微秒,即10ms)会被记录。LATENCY LATEST:看Redis内部的主线程延迟事件,包括fork、aof-write、expire-cycle等。
如果SLOWLOG里没有大量记录,INFO commandstats里也没发现某个命令的调用耗时异常,LATENCY LATEST基本干净——那Redis处理能力本身没问题。问题更可能出在“Redis发出的响应包没能按时到达客户端”。
3.3 Redis 6.0+和7.0的新排查手段
Redis 6.0及以上版本,INFO里多了一些有用的网络指标:
total_net_input_bytes/total_net_output_bytes:网络吞吐量。total_reads_processed/total_writes_processed:读事件和写事件的处理次数。io_threaded_reads_processed/io_threaded_writes_processed:如果开启了IO多线程,这个值帮助判断是否有线程处理瓶颈。
Redis 7.0还支持INFO DEBUG下的更多内部参数,但日常排查用不上那么多,知道这两个就够了。
这里要特别说一下,Redis单线程处理模型下,一个慢命令会阻塞所有后续命令。典型场景是KEYS *、HGETALL一个超大hash、或者DEL一个超大key。这类命令导致的超时表现是:所有的客户端同时卡顿,CPU不一定飙高,但请求RT曲线出现“断崖式上涨”。这种情况下,与其怀疑网络丢包,不如先排查慢查询和大key。用redis-cli --bigkeys可以扫描大key,这个命令在生产环境可以跑,但建议流量低峰期执行。
4. 系统与内核层面的隐藏因素
4.1 socket缓冲区与TCP窗口
网络丢包不一定发生在物理链路,也可能发生在操作系统内部。
每个TCP连接都有接收缓冲区和发送缓冲区。如果应用层读取数据不及时,接收缓冲区满了之后,TCP会通过窗口通告机制告诉对端“别发了,我处理不过来”,之后对端发送的数据就会被丢弃。在Redis场景下,如果业务侧使用了连接池但并发太高,撑爆了Redis服务器的接收缓冲区,表现就是客户端发出去的请求丢失、超时重传。
可以用下面的命令查看当前socket缓冲区设置:
bash复制sysctl net.core.rmem_max
sysctl net.core.wmem_max
sysctl net.ipv4.tcp_rmem
sysctl net.ipv4.tcp_wmem
默认值通常是212992(约208KB)到6291456(6MB)之间。如果你的业务单次请求量大,或者使用了MGET、Pipeline、Lua Script这些批量操作,建议把socket缓冲区调大。
4.2 TCP重传参数的影响
Linux内核里有两个参数直接影响超时感知:
bash复制net.ipv4.tcp_retries1 = 3
net.ipv4.tcp_retries2 = 15
tcp_retries1表示在底层网络出现问题时,TCP经过多少次重传后主动通知上层“链路可能有问题”;tcp_retries2表示放弃连接前最多重传多少次。
重点在tcp_retries2。默认15次,但重传的超时是指数退避的,算下来最长可能超过15分钟才放弃连接。这意味着:如果链路彻底断开,客户端应用可能要在15分钟后才收到连接失败的异常。 你的业务超时设置如果小于这个时间,应用层会先超时,但底层连接其实还在默默重传,导致连接长时间挂起、连接池被占满,后续所有请求都排队等连接。这也是“网络丢包引发请求超时”最常见的次生灾害。
4.3 网卡环形缓冲区与中断合并
物理网卡上有环形缓冲区(Ring Buffer),数据包到达网卡后先写入这里,再通过硬中断通知内核处理。如果环形缓冲区满了,新到的包会被直接丢弃。查看方式:
bash复制ethtool -g eth0
如果看到RX的current值相对max值偏小,且网卡所在机器的/proc/net/softnet_stat里面有被drop的计数,可以考虑调大:
bash复制ethtool -G eth0 rx 4096 tx 4096
另一个影响是中断合并(Interrupt Coalescing),也就是网卡攒了一批包再通知内核处理。对Redis这种低延迟、高频小包的服务来说,过大的合并阈值会把延迟从微秒级拉到毫秒级。查看和关闭:
bash复制ethtool -c eth0
ethtool -C eth0 rx-usecs 0 tx-usecs 0
4.4 虚拟机与容器环境下的特殊“陷阱”
如果你的Redis运行在虚拟机或容器里,还需要额外排查宿主机层面的影响。
虚拟化的网络IO路径比物理机长得多,常见的坑有两个。第一是宿主机网卡半虚拟化驱动的队列数不足,多核CPU争抢同一个队列,表现为CPU不高的场景下网络延迟也莫名变大。第二是公有云的带宽限速策略,某些云厂商默认带宽只有1Gbps,你跑满之后表现为“瞬间丢包、但CPU/内存完全正常”。
判断方法很简单:在宿主机和虚拟机里同时pingRedis的IP,对比延迟。宿主机正常、虚拟机丢包,问题就出在虚拟化网络路径上。
5. 完整案例复盘:一次真实的Redis超时排查过程
5.1 现象描述
某业务系统上线的第三周开始,运营反馈“后台偶尔打不开,白屏好几秒”。开发排查后发现,应用日志里有零星的Redis read timed out,大概每分钟出现1-3次,每次影响1-2个请求。Redis所在服务器CPU不到10%,内存充足,慢查询日志基本为空,maxclients也没到上限。
所有迹象都指向网络,但运维反馈“交换机没有告警”,于是排查陷入僵局。
5.2 排查过程
第一步,在出问题的应用服务器和Redis服务器上做快速体检。INFO stats里没有异常,SLOWLOG为空,Redis自身很“无辜”。
第二步,用ping -i 0.2 -c 1000测两端延迟。结果丢包率为0.2%,看起来不高,但平均延迟的max值达到了30ms,明显有抖动。mtr显示第二跳设备丢包率2%,后续跳数正常。
第三步,在客户端和Redis服务器上同时抓包。抓取10分钟,发现每隔几十秒就会出现一次TCP重传,重传间隔按200ms、400ms的指数规律递增。确认丢包存在,且丢包方向是从Redis到客户端的方向。
第四步,检查两端服务器的ethtool -S eth0,发现Redis服务器的rx_dropped计数持续增长。上一步mtr中显示的第二跳设备,是客户内网的一台交换机,它的上行端口流量接近打满。
5.3 根因与解决
根因是:业务高峰期,应用服务器向Redis发送的流量加上其他业务的流量,总和超过了内网交换机两个端口之间的带宽容量,导致交换机尾部丢弃(Tail Drop)。Redis正常回包时,回包在交换机处被丢弃,客户端直到TCP重传超时才感知到。
解决分三步:
- 临时方案:把Redis客户端连接池从最大50调整到30,降低总并发连接数,减少交换机的压力。
- 长期方案:梳理业务流量,把非核心流量迁移到另一段独立的网络链路,让Redis流量独占交换机端口带宽。
- 兜底方案:客户端增加快速失败+重试一次的逻辑,超时阈值从3秒降到1.5秒,重试请求走“备用连接”,避免单次网络抖动拖垮整个业务。
调整后,超时告警归零,再也没出现白屏。
5.4 这波排查里最关键的三个判断
事后复盘,有三个判断是决定成败的。
第一个判断是别只看丢包率,要看延迟抖动。0.2%的丢包率看着人畜无害,但结合max延迟30ms一起看,链路拥塞的真相就露出来了。如果只盯着丢包率数字,可能还要排查好几天。
第二个判断是抓包要双向同时抓,而且要在不同层面对比。只抓客户端一侧,你只能确认“响应没按时回来”,但不知道是Redis没发出来还是链路丢了。同时抓两边,对比数据包序列号和时间戳,能精确定位丢包方向。
第三个判断是应用层和内核层的信息要结合起来看。应用层日志看到的是“超时”,内核层的rx_dropped和tcpdump的重传包揭示的是“哪里丢了”,交换机端口的流量统计揭示的是“为什么丢”。三者的数据链闭环,问题就无处遁形。
6. 预防与长期治理:别等超时了才去查
6.1 客户端参数配置建议
在客户端侧,有几个参数值得根据网络实际质量去配置,而不是使用默认值。
- 连接超时:建议500ms到1s之间。如果内网环境稳定,500ms足够;跨机房或公网,可以放宽到2s。
- 读取超时:日常业务建议2s以内。如果你发现读取超时频繁触发,要先排查网络和Redis,而不是调大超时。
- 连接池最大连接数:建议按“QPS * 单请求耗时 * 1.5”来估算,不要让连接数无限大,否则Redis服务端的文件描述符和连接管理本身会成为瓶颈。
- 重试策略:对外部调用可以重试,但要控制次数。我一般只重试一次,且重试请求要走到独立的网络链路或连接,防止同一条链路连续丢包。
6.2 监控指标怎么搭
没有监控,丢包问题就是“薛定谔的丢包”——只有出问题的时候才存在。日常需要盯几个关键指标:
- TCP重传率:用
/proc/net/netstat里的TCPLostRetransmit / TCPAttemptFails等计数器,每分钟取一次差值,换算成每秒重传数。超过一定阈值就告警。 - ping丢包率和RTT波动:对核心Redis实例,每30秒ping一次,记录丢包率、max/min RTT差值。丢包率>0.1%或max-min超过5ms就告警。
- Redis的
INFO stats里的total_net_input_bytes和total_net_output_bytes:观察流量变化趋势,提前发现带宽打满的苗头。 - 交换机端口
Discards和Errors计数:网管系统一般都有,没有的话建议接上。
6.3 从架构层面减少网络依赖
网络永远不可能做到100%不丢包,所以架构设计上要做冗余。
多级缓存是性价比最高的方案。热点数据在应用本地加一层Caffeine(或Guava Cache),命中率能做到90%以上,Redis的连接数和网络流量大幅下降,留给网络抖动的空间就大了。
Redis部署上,如果是单机,至少要做主从+哨兵;如果是集群版,客户端要配置好路由。一旦某台Redis的网络路径出问题,流量能自动切换。这里有个细节:哨兵本身的网络探测也依赖TCP,如果网络丢包导致哨兵误判主节点宕机,反而会触发不必要的故障转移。 调大sentinel-down-after-milliseconds(默认5000ms),以及sentinel failover-timeout(默认180000ms),可以降低误判概率。
6.4 云环境下的特殊注意点
云上部署Redis还要考虑一个因素:云平台的虚拟网络(VPC)路径比自建机房复杂,中间可能经过SDN网关、NFV节点等。这些组件偶发延迟抖动是常见的,尤其是在其他租户“吵闹邻居”影响下,网络热点迁移也可能导致瞬间延迟变大。
如果你在云上遇到疑似丢包问题,优先做以下操作而不是自己抓包:
- 确认客户端和Redis实例在同一可用区(AZ)内,跨AZ的访问延迟必然比同AZ高。
- 检查安全组配置,安全组规则太多或太复杂(规则数超过100条),某些云平台的网络ACL处理延迟会明显增大。
- 用云平台自带的网络监控工具,比如阿里云的VPC流日志、腾讯云的网络探测,这些工具能直接看到丢包发生在哪个虚拟网络节点上,比自己在ECS里抓包高效得多。
7. 自查清单与最终心得
最后整理一份完整的排查顺序自查清单,遇到Redis请求超时,按这个顺序走基本不会漏掉关键点:
| 排查层级 | 关键命令/工具 | 重点观察 |
|---|---|---|
| Redis自身 | INFO stats、SLOWLOG、LATENCY LATEST |
慢查询、命令耗时、事件延迟 |
| 客户端应用 | 日志、连接池参数、超时参数 | 超时类型(connect/read/write) |
| 网络连通性 | ping -i 0.2 -c 1000 |
丢包率、max/avg/RTT波动 |
| 网络路径 | mtr -rwc 100 |
哪一跳丢包、延迟突变点 |
| TCP层 | tcpdump抓包 |
TCP重传序列及指数退避间隔 |
| 内核/系统 | netstat -s、ethtool -S |
重传计数、rx_dropped、软中断丢包 |
| 物理/虚拟设备 | 交换机端口统计、云平台监控 | Discards、Errors、限速指标 |
这套流程适用于任何TCP服务的超时排查,不只是Redis。MySQL查询超时、MongoDB连接超时、Kafka生产消费超时,底层逻辑完全一样:先确认服务端没事,再用ping/mtr/tcpdump定位网络,最后结合系统和内核数据确认根因。
最后再分享一点个人体会:排查超时问题,最怕的不是链路复杂,而是思维定式。报了Redis超时就去调Redis参数,这种“头痛医头”的做法我见得太多,最后往往把超时从2秒改到10秒,问题依然在,只是更难发现了。把网络当成一等公民看待,给网络留出排查时间和工具,才是治本之道。以后谁再报“Redis超时”,你先别急着改配置,上文这套完整流程走一遍,大概率能在半小时内抓到真凶。
