Linux 服务器性能排查案例分析
以下是 2024–2026 年期间线上真实遇到过的几种典型性能问题案例,按出现频率从高到低排列。每案例都包含:
- 告警/现象描述
- 第一波指标快照(当时看到的典型数值)
- 排查路径(实际走的顺序)
- 根因分析
- 最终解决方案
- 复盘学到的关键教训
案例 1 – 最经典:日志狂写导致 iowait 爆炸(占比最高)
告警现象
- Prometheus 告警:iowait > 40% 持续 10 分钟
- 业务方反馈:接口平均延迟从 30ms → 800ms–2s,P99 飙到 8–12 秒
- 机器负载:24 核,load average 45–60
第一波快照(大致数值)
text
uptime
12:34:56 up 45 days, load average: 52.3, 48.7, 46.1
iostat -x 1 5
Device r/s w/s rkB/s wkB/s await %util
nvme0n1 120 4500 1800 82000 68ms 98.7%
vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
18 42 0 1.2g 180m 3.8g 0 0 240 4200 1800 32000 12 18 5 65 0
top 第一屏
%Cpu(s): 8.2 us, 6.5 sy, 0.0 ni, 18.4 id, 66.9 wa, 0.0 hi, 0.0 si, 0.0 st排查路径(实际顺序)
- iostat → %util 接近 100%,w/s 极高 → 写压力为主
- iotop -o → 前三名全是 java 进程(应用日志 + tomcat access log)
- du -sh /var/log/* → 应用日志目录 10 分钟涨了 38GB
- lsof | grep deleted → 发现 catalina.out 被 logrotate 删了,但还在写(deleted)
根因
- log4j2 级别设为 DEBUG
- logrotate 使用 mv 而不是 copytruncate
- catalina.out 被 mv 后文件描述符未释放,持续追加写入
解决方案
- 紧急:kill -USR1 tomcat pid(让它重新打开日志文件)
- 临时:echo “” > catalina.out(清空不删文件)
- 永久:
- log4j2 改成 info + AsyncAppender
- logrotate 配置 copytruncate
- 应用日志目录单独挂载到高性能盘或 tmpfs(可选)
复盘教训 “日志永远是 IO 第一杀手” —— 任何线上环境第一次性能问题都应该先怀疑日志。
案例 2 – 数据库同步写把机器拖死
告警现象
- 数据库实例延迟从 2ms → 800ms–3s
- 应用报大量超时
- 机器 iowait 55–75%,load 38(16 核)
第一波快照
text
iostat -x 1 5
Device r/s w/s rkB/s wkB/s await %util
sda 180 3200 2400 68000 92ms 96.4%
free -wh
total used free shared buff/cache available
Mem: 128Gi 18Gi 82Gi 2.1Gi 28Gi 105Gi
Swap: 4.0Gi 0B 4.0Gi
vmstat 1 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
6 38 0 82.1g 1.8g 26.4g 0 0 180 6800 2100 28000 9 11 4 76 0排查路径
- iostat → 写压力极大,w/s 高,await 很高
- iotop → mysqld 写最多
- SHOW GLOBAL STATUS LIKE ‘Innodb_log_waits’; → 值持续增长
- SHOW VARIABLES LIKE ‘%flush_log%’; → innodb_flush_log_at_trx_commit = 1 SHOW VARIABLES LIKE ‘sync_binlog’; → sync_binlog = 1
根因 双 1 配置(每次事务都刷 redo log + binlog),高并发下每次提交都强制 fsync,导致随机写 IO 爆炸。
解决方案
- 紧急:set global innodb_flush_log_at_trx_commit=2; set global sync_binlog=1000; (业务方评估可接受 1 秒数据丢失风险)
- 永久:my.cnf 修改后重启
- 后续加了 semi-sync replication + 异步 binlog 传输
复盘教训 “数据库双 1 是性能毒药” —— 除非强一致性金融场景,否则默认就不要用。
案例 3 – TIME_WAIT 堆积导致新连接建立极慢
告警现象
- 新用户接入慢,已连接用户正常
- 部分地区用户报“连接超时”
- Nginx error_log 大量 “connection refused” 或 “accept() failed”
第一波快照
text
ss -s
TCP: 124800 (estab 118200, closed 6500, orphaned 0, timewait 62000/0)
netstat -s | grep -i listen
18432 times the listen queue of a socket overflowed
9200 SYNs to LISTEN sockets dropped
uptime
load average: 9.2, 8.7, 8.4(16 核)排查路径
- ss -s → TIME_WAIT 6.2 万,listen queue overflow 明显
- ss -ltnp | grep :443 → nginx listen 0.0.0.0:443
- sysctl net.ipv4.tcp_tw_reuse = 0 net.ipv4.tcp_fin_timeout = 60
根因 高并发短连接场景 + 默认 tcp_fin_timeout=60s + tcp_tw_reuse 未开启 → TIME_WAIT 堆积耗尽端口。
解决方案
Bash
sysctl -w net.ipv4.tcp_tw_reuse=1
sysctl -w net.ipv4.tcp_fin_timeout=15
sysctl -w net.core.somaxconn=16384
sysctl -w net.ipv4.tcp_max_syn_backlog=16384Nginx 配置:
text
worker_connections 16384;复盘教训 “高并发短连接场景一定要开启 tcp_tw_reuse + 调小 fin_timeout”
总结:高频性能问题 Top 3 口诀
- 先怀疑日志(debug + 同步写 + deleted 文件)
- 再怀疑数据库双 1(innodb_flush_log + sync_binlog)
- 最后怀疑连接堆积(TIME_WAIT / listen backlog)
你目前服务器最像哪一个案例?