TCP 排障入门指南(新人版)

TCP 排障入门指南

本文从团队知识库中提炼而来,面向刚接触网络排障的新同学。不讲大而全的理论,只讲排障时真正用得上的东西。

第一课:排障的核心思维

记住三句话,比记住任何工具都重要:

  1. 一个错误现象可能对应多个完全不同的根因 —— 不要看到报错就下结论
  2. 不要相信报错信息的字面意思 —— 比如 net_write_timeout 报错不一定是超时
  3. 拿证据推进问题 —— 每一步推理都要有抓包、日志、堆栈等证据支撑

排障工具的优先级:

1
2
3
4
5
6
7
抓包(tcpdump/wireshark)  ← 网络层面的终极证据,优先用

堆栈/火焰图(perf/jstack) ← 定位代码热点

日志分析 ← 但要警惕日志被吃掉的情况

监控指标 ← 宏观趋势,不够精确

第二课:你必须知道的 TCP 基础

三次握手

1
2
3
4
Client              Server
|--- SYN ---------->| Client 进入 SYN_SENT
|<-- SYN+ACK -------| Server 进入 SYN_RECV(半连接队列)
|--- ACK ---------->| 双方进入 ESTABLISHED(全连接队列)

握手失败的常见原因:

现象 原因 怎么查
精确 1s/3s/7s 超时 SYN 被丢弃后重传 全连接队列满?防火墙 DROP?
Connection refused 端口没人监听 ss -lntp 看端口
偶发连接超时 半连接/全连接队列满 netstat -s | grep listen
NAT 下偶发不通 tcp_tw_recycle 丢 SYN netstat -s | grep "time stamp"

新人必记:碰到精确的 1s、3s、7s 超时,几乎可以断定是 SYN 丢包重传。SYN 重传间隔是 1s→2s→4s→8s 指数退避。

四次挥手

1
2
3
4
5
主动方              被动方
|--- FIN ---------->| 主动方: FIN_WAIT_1
|<-- ACK -----------| 被动方: CLOSE_WAIT ← 最常见的堆积点
|<-- FIN -----------| 被动方: LAST_ACK
|--- ACK ---------->| 主动方: TIME_WAIT(等 60 秒)
状态堆积 说明 排查
大量 CLOSE_WAIT 你的应用没调 close() ss -tp 看是哪个进程
大量 TIME_WAIT 短连接太多,通常无害 tcp_tw_reuse 缓解

新人必记:CLOSE_WAIT 是被动关闭方的状态,问题一定在你这边的应用代码。

传输性能公式

1
理论最大吞吐 ≈ min(发送窗口, 接收窗口, 拥塞窗口) / RTT

速度上不去时检查:

  • Buffer 太小ss -tm 看 skmem,检查 tcp_rmem / tcp_wmem
  • RTT 太大:RTT 越大,慢启动越久,丢包恢复越慢
  • 丢包:一旦丢包 RTO 指数退避(最大 120 秒),速度断崖式下降

第三课:五个必会的排障命令

1. ss —— 看连接状态(替代 netstat)

1
2
3
4
5
6
7
8
# 看监听端口的全连接队列(Recv-Q 是当前排队数,Send-Q 是队列上限)
ss -lnt

# 看所有 TCP 连接的详细信息(含 buffer、RTT、拥塞窗口)
ss -tinp

# 看连接统计
ss -s

2. netstat -s —— 看协议栈统计

1
2
3
4
5
6
7
8
9
10
11
# 全连接队列溢出(数字在增长就是有问题)
netstat -s | grep "listen queue"

# SYN 被丢弃
netstat -s | grep "SYNs to LISTEN"

# tcp_tw_recycle 导致的丢包
netstat -s | grep "passive connections rejected because of time stamp"

# 丢包/重传汇总
netstat -s | egrep -i "drop|retran|overflow|reject"

3. tcpdump —— 抓包

1
2
3
4
5
6
7
8
9
10
11
# 抓指定端口,保存为文件(用 wireshark 打开分析)
tcpdump -i eth0 port 3306 -s0 -w mysql.pcap

# 抓所有网卡(别忘了 lo,nginx→tomcat 走的是 lo)
tcpdump -i any port 8080 -s0 -w all.pcap

# 只抓 RST 包
tcpdump 'tcp[tcpflags] & (tcp-rst) != 0'

# 只抓 SYN 包(排查握手问题)
tcpdump 'tcp[tcpflags] & (tcp-syn) != 0'

新人必记:抓包时一定要抓 所有网卡-i any),不要只抓 eth0。很多内部通信走 lo 网卡,只抓 eth0 会漏掉关键包。

4. tshark —— 命令行分析抓包

1
2
3
4
5
6
7
8
# 统计每个连接的 RT
tshark -r file.pcap -q -z io,stat,1

# 过滤 MySQL 错误
tshark -r file.pcap -Y "mysql.error_code != 0"

# 看 TCP 重传
tshark -r file.pcap -Y "tcp.analysis.retransmission"

5. 内核参数快速检查

1
2
3
4
5
6
7
# 一键查看关键 TCP 参数
sysctl net.ipv4.tcp_tw_recycle # 必须是 0!
sysctl net.core.somaxconn # 全连接队列上限,建议 ≥ 2048
sysctl net.ipv4.tcp_max_syn_backlog # 半连接队列
sysctl net.ipv4.tcp_retries2 # 重传放弃次数,内网建议 5-10
sysctl net.ipv4.tcp_rmem # 接收 buffer
sysctl net.ipv4.tcp_wmem # 发送 buffer

第四课:按现象查问题(速查表)

连不上

现象 第一步 第二步
Connection refused ss -lntp 看端口是否在监听 检查进程是否存活
超时 1s/3s/7s netstat -s | grep listen 看队列溢出 抓包确认 SYN 是否被丢
偶发超时(NAT 环境) sysctl net.ipv4.tcp_tw_recycle 如果是 1 立即改 0
部分机器不通 traceroute + 两端同时抓包 检查路由/ARP/交换机

连上了但是断开

现象 第一步 第二步
Connection reset 抓包看 RST 是谁发的 检查防火墙/中间设备
大量 CLOSE_WAIT ss -tp 找到对应进程 检查代码是否漏了 close()
服务切换后长时间不恢复 检查 tcp_retries2 调小到 5-10

连上了但是慢

现象 第一步 第二步
RT 偶发 40ms 毛刺 抓包看是否 Delayed ACK 设置 TCP_NODELAY
传输速度上不去 ss -ti 看 cwnd/ssthresh/rto 检查 rmem/wmem 是否太小
速度突然下降 抓包看是否有重传 检查 RTO 是否飙到 120s

第五课:四个真实案例(必读)

案例 1:报错说 net_write_timeout,其实是被 kill 了

迁移工具报 Consider raising value of 'net_write_timeout',调大参数无效。抓包发现 MySQL Server 在传数据途中夹带了 FIN 包,查 DB 日志发现是用户自己的监控脚本 kill 了慢查询。

教训:JDBC streaming 模式下任何连接异常都报 net_write_timeout,不要被字面意思骗了。抓包看谁先发 FIN 是铁证。

详见:历时 5 年的 net_write_timeout 分析

案例 2:Sysbench 性能只有预期的 10%

processlist 全是 Opening tables,调大 table_open_cache 无效。最后发现是 --tables=64 写错了(实际只有 32 张表),加上 --mysql-ignore-errors=all 把 Error 1146 全吞了。

教训:抓包能看到 Error 1146 和异常 RT(2200ms vs 正常 0.1ms),即使不知道问题在哪,抓包也能发现异常。

详见:Sysbench Opening Tables 卡慢

案例 3:数据库重启后业务 15 分钟才恢复

数据库 crash 重启后,业务长时间报错。原因是 tcp_retries2 默认 15,TCP 需要约 924 秒(15 分钟)才能感知到连接断开。

教训:内网把 tcp_retries2 改成 5-10。应用层心跳比 TCP Keepalive 更可靠。

详见:长连接黑洞

案例 4:开了 tcp_tw_recycle,NAT 下偶发连不上

服务端开了 tcp_tw_recycle,NAT 后面多台客户端的 TCP timestamp 不一致,导致 SYN 被 PAWS 校验丢弃。

教训:永远不要开 tcp_tw_recycle。4.12 内核已删除此参数。用 netstat -s | grep "time stamp" 诊断。

详见:tcp_tw_recycle + NAT 导致 SYN 丢包

第六课:新人上手清单

拿到一台新机器,先跑一遍这些命令建立基线:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 看内核版本(4.12 以下要特别注意 tcp_tw_recycle)
uname -r

# 2. 检查危险参数
sysctl net.ipv4.tcp_tw_recycle 2>/dev/null # 必须是 0 或不存在
sysctl net.core.somaxconn # 太小(128)要调大

# 3. 看当前连接状态分布
ss -s

# 4. 看是否有队列溢出(记下数字,过一会再看是否增长)
netstat -s | egrep -i "listen|overflow|drop|retran"

# 5. 看监听端口的队列使用情况
ss -lnt

延伸阅读

本指南的所有内容都来自团队知识库 ~/case/ 下的原始文档,想深入学习可以按以下路径:

  1. TCP 连接原理网络/就是要你懂TCP--连接和握手.md网络/就是要你懂TCP--半连接队列和全连接队列.md
  2. TCP 性能网络/就是要你懂TCP--性能和发送接收Buffer的关系.md网络/就是要你懂TCP--最经典的TCP性能问题.md
  3. 抓包技巧工具/就是要你懂抓包--WireShark之命令行版tshark.md网络/如何从几百万个抓包中找到一个异常的包.md
  4. 排障方法论网络/举三反一--从理论知识到实际问题的推导.md网络/程序员如何学习和构建网络知识体系.md

本文由 LLM 基于 wiki 知识库自动生成,最后更新:2026-04-07