Linux 服务器性能排查案例分析

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

排查路径(实际顺序)

  1. iostat → %util 接近 100%,w/s 极高 → 写压力为主
  2. iotop -o → 前三名全是 java 进程(应用日志 + tomcat access log)
  3. du -sh /var/log/* → 应用日志目录 10 分钟涨了 38GB
  4. lsof | grep deleted → 发现 catalina.out 被 logrotate 删了,但还在写(deleted)

根因

  • log4j2 级别设为 DEBUG
  • logrotate 使用 mv 而不是 copytruncate
  • catalina.out 被 mv 后文件描述符未释放,持续追加写入

解决方案

  1. 紧急:kill -USR1 tomcat pid(让它重新打开日志文件)
  2. 临时:echo “” > catalina.out(清空不删文件)
  3. 永久:
    • 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

排查路径

  1. iostat → 写压力极大,w/s 高,await 很高
  2. iotop → mysqld 写最多
  3. SHOW GLOBAL STATUS LIKE ‘Innodb_log_waits’; → 值持续增长
  4. 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 爆炸。

解决方案

  1. 紧急:set global innodb_flush_log_at_trx_commit=2; set global sync_binlog=1000; (业务方评估可接受 1 秒数据丢失风险)
  2. 永久: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 核)

排查路径

  1. ss -s → TIME_WAIT 6.2 万,listen queue overflow 明显
  2. ss -ltnp | grep :443 → nginx listen 0.0.0.0:443
  3. 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=16384

Nginx 配置:

text
worker_connections 16384;

复盘教训 “高并发短连接场景一定要开启 tcp_tw_reuse + 调小 fin_timeout”


总结:高频性能问题 Top 3 口诀

  1. 先怀疑日志(debug + 同步写 + deleted 文件)
  2. 再怀疑数据库双 1(innodb_flush_log + sync_binlog)
  3. 最后怀疑连接堆积(TIME_WAIT / listen backlog)

你目前服务器最像哪一个案例?