redis高并发丢包大概率是内核全连接队列溢出所致,因net.core.somaxconn过小导致已完成三次握手的连接被静默丢弃,表现为超时或connection refused,而tcpdump抓不到syn-ack或rst;需同步调大net.core.somaxconn、redis tcp-backlog配置及进程ulimit -n,并确保bind地址与压测路径一致。

Redis 集群在高并发下丢包,大概率不是网络设备问题,而是内核全连接队列溢出导致连接被静默丢弃——net.core.somaxconn 值太小是首要排查点。
为什么 Redis 会“丢包”但 netstat 看不出 SYN 重传?
Redis 是单线程处理连接(6.0+ 可配 IO 线程,但 accept 仍主线程),当并发建连速度 > accept() 处理速度时,已完成三次握手的连接会堆积在全连接队列。队列满后,内核直接丢弃新连接,不发 RST,客户端表现为超时或 Connection refused,而 tcpdump 抓不到对应 SYN-ACK 或 RST,容易误判为网络层丢包。
- 典型现象:
netstat -s | grep -i "listen overflows"输出非零(如listen overflows: 42) - 关键区别:这不是 SYN flood(半连接队列问题),而是
SYNs to LISTEN sockets dropped后的后续丢弃,属于全连接队列瓶颈 - Redis 默认不显式设置
listen()的backlog参数,实际取值由 glibc 决定(旧版常为 128),若内核net.core.somaxconn也卡在默认 128,极易溢出
怎么改 net.core.somaxconn 才真正生效?
只改内核参数没用,必须同步满足三个条件:
- 临时生效:
sudo sysctl -w net.core.somaxconn=65535(立即起效,重启失效) - 持久化:
echo 'net.core.somaxconn = 65535' | sudo tee -a /etc/sysctl.d/99-redis.conf && sudo sysctl --system(推荐用/etc/sysctl.d/而非直接改/etc/sysctl.conf) - Redis 层必须显式声明 backlog:从 Redis 6.2 开始支持
tcp-backlog配置项,在redis.conf中加一行tcp-backlog 65535,然后重启 Redis;老版本需重新编译或确认所用 glibc 版本是否支持自动提升
验证是否生效:ss -lnt | grep :6379 输出中 Send-Q 列应显示 65535(而非 128 或 511)。
ss -lnt 里 Recv-Q 长期 > 0 说明什么?
Recv-Q 是当前等待 accept() 的连接数,持续非零代表 Redis 处理不过来,不只是队列长度问题,还可能暴露真实瓶颈:
- 若
Recv-Q接近Send-Q(如 65530/65535),说明队列确实压满,somaxconn还不够大或需继续调优 - 若
Recv-Q稳定在 100~500 且波动大,更可能是 Redis 自身阻塞:检查是否有慢命令(SLOWLOG GET)、持久化阻塞(bgrewriteaof)、或 AOF fsync 延迟(info persistence中aof_delayed_fsync) - 别忽略网卡队列:
net.core.netdev_max_backlog也要同步调大(建议 ≥20000),否则数据包在进协议栈前就被网卡丢弃,ss看不到,但/proc/net/snmp中TCPBacklogDrop会增长
调大了还是丢连接?检查这三个易漏点
很多人改完 net.core.somaxconn 和 Redis 配置,压力测试仍失败,往往栽在这三处:
- 文件描述符限制未同步:Redis 进程级
ulimit -n必须 ≥net.core.somaxconn× 1.2(每个连接至少占 1 fd,还有内部 fd),否则accept()直接失败报EMFILE - Redis 绑定的是
127.0.0.1但压测走的是外网 IP:本地回环走的是特殊路径,不受net.core.somaxconn限制,务必确认压测目标地址与 Redisbind配置一致 - 集群模式下只改了某几个节点:Redis Cluster 要求所有节点配置一致,否则客户端重定向到低配节点时立刻触发拒绝
真正起作用的永远是「内核参数、应用 backlog、进程资源限制」三者的最小值,任何一环卡住,前面全白调。











