Linux 系统 Bug 调试操作手册
适用环境 :Ubuntu 20.04 / 22.04 / 24.04(systemd 体系)
目标读者 :有基础 Linux 经验的后端开发,目标是能独立定位中低复杂度问题
覆盖问题 :崩溃(Crash)、挂起(Hang)、OOM、性能劣化、服务启动失败
工具原则:优先系统自带工具(procps / iproute2 / systemd / util-linux),少量常用诊断工具通过官方 apt 源安装,不依赖任何第三方商业软件
使用约定
- 每个命令后跟一行 「看什么」,说明输出里该盯住的关键字或数值。
- 命令中的
<svc>指 systemd 服务名,<PID>指进程号,按实际替换。 - 排查顺序永远是:先保现场 → 再看资源 → 后动进程。没采集信息就重启,等于销毁证据。
- 正文含 6 张 Mermaid 流程图(总决策树 + 五类问题专项流程)。GitHub / GitLab / Typora 原生渲染;VS Code 需装 Mermaid 预览插件。
1. 信息采集清单(到场后前 60 秒)
不管什么问题,先跑一遍下面的快照命令。输出本身就是第一手证据,也能让后续排查少绕路。
| # | 命令 | 用途 | 预期输出里的关键字 |
|---|---|---|---|
| 1 | uptime |
总览负载与运行时长 | load average: 三个数是否递增;是否超过 CPU 核数 |
| 2 | free -h |
内存与 swap 概况 | available 是否接近 0;Swap 是否用满 |
| 3 | df -h |
磁盘空间 | 是否有分区 Use% 100% |
| 4 | df -i |
inode 使用率 | IUse% 是否 100%(空间没满也写不进文件) |
| 5 | vmstat 1 5 |
CPU / 内存 / 换页动态趋势 | r/b 列、us sy wa st、si so |
| 6 | `dmesg -T | tail -50` | 内核近期消息(带时间戳) |
| 7 | `journalctl -p 3 -xb | tail -50` | 本次开机以来 error 级日志 |
| 8 | systemctl --failed |
处于失败状态的 unit | 失败服务列表 |
| 9 | `ps aux --sort=-%mem | head -15` | 内存占用 Top 进程 |
| 10 | `ps aux --sort=-%cpu | head -15` | CPU 占用 Top 进程 |
| 11 | ss -lntp |
监听端口与对应进程 | 预期端口是否都在;有无意外监听 |
| 12 | ss -s |
连接状态汇总 | timewait / close-wait 数量是否异常 |
一次性快照(可直接粘贴执行):
bash
uptime; free -h; df -h; df -i; ss -s; dmesg -T | tail -30
看什么:一眼扫过去是否已有明显异常------磁盘满、内存空、连接堆积、内核报错,任何一条命中就直接指向对应章节。
补充:如果机器装了 sysstat(见 §4),历史数据是"案发当时"的唯一证人:
bash
sar -u -f /var/log/sysstat/sa$(date -d yesterday +%d)
看什么:昨天同时段的 CPU 曲线,和今天对比,确认"劣化"是从哪一刻开始的。
2. 分层排查流程图(决策树)
按照「内核层 → 系统资源层 → 服务层 → 代码层」逐层缩小范围:
text
故障现象(告警 / 用户反馈 / 巡检发现)
│
├─ 第 1 步:保护现场,跑 §1 信息采集清单
│
├─ 第 2 步:进程还在吗?
│ 命令:ps aux | grep <进程名> 或 systemctl status <svc>
│ │
│ ├─ 【不在了 / 反复重启中】
│ │ ├─ journalctl -k | grep -i 'out of memory' 有命中?
│ │ │ ├─ 有 ────────────► OOM 专线 ──────► §3.3
│ │ │ └─ 无 ↓
│ │ ├─ dmesg / journalctl -k 里有 segfault / signal 记录?
│ │ │ ├─ 有 ────────────► 崩溃专线 ──────► §3.1
│ │ │ └─ 无 ↓
│ │ └─ 退出码非 0、进程自行退出
│ │ └─────────────────► 启动失败专线 ──► §3.5
│ │
│ └─ 【还在,但行为异常(超时 / 无响应 / 变慢)】
│ │
│ ├─ 整机忙吗?看 uptime + vmstat 1
│ │ ├─ load 高、r 列大、us+sy 高 ─► CPU 瓶颈 ────┐
│ │ ├─ wa 高、b 列大、iostat %util≈100 ► IO 瓶颈 ─┼► 性能劣化专线 §3.4
│ │ ├─ si/so 持续非 0 ─► swap 换页,内存压力 ─────┘(并参考 §3.3)
│ │ └─ 整机不忙,只有单个服务异常 ↓
│ │
│ └─ 进程"活着但不干活"?
│ ├─ 连接堆积:大量 CLOSE_WAIT / Recv-Q 积压 ─► 挂起专线 §3.2
│ ├─ 线程大量处于 D 状态(不可中断睡眠)─────► 挂起专线 §3.2
│ └─ 只是部分请求慢 ─────────────────────────► 性能劣化专线 §3.4
│
└─ 第 3 步:单服务问题还是全局问题?
├─ 单服务 → 重点在应用层:日志 → strace/gdb → core dump
└─ 全机器 → 重点在系统层:按 USE 方法扫一遍
每种资源(CPU/内存/磁盘/网络)查三件事:
使用率(Utilization)、饱和度(Saturation)、错误数(Errors)
决策树可视化版(Mermaid):
#mermaid-svg-2qRVdlVYKqkHfogS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2qRVdlVYKqkHfogS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2qRVdlVYKqkHfogS .error-icon{fill:#552222;}#mermaid-svg-2qRVdlVYKqkHfogS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2qRVdlVYKqkHfogS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2qRVdlVYKqkHfogS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS .marker.cross{stroke:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2qRVdlVYKqkHfogS p{margin:0;}#mermaid-svg-2qRVdlVYKqkHfogS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster-label text{fill:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster-label span{color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster-label span p{background-color:transparent;}#mermaid-svg-2qRVdlVYKqkHfogS .label text,#mermaid-svg-2qRVdlVYKqkHfogS span{fill:#333;color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .node rect,#mermaid-svg-2qRVdlVYKqkHfogS .node circle,#mermaid-svg-2qRVdlVYKqkHfogS .node ellipse,#mermaid-svg-2qRVdlVYKqkHfogS .node polygon,#mermaid-svg-2qRVdlVYKqkHfogS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .rough-node .label text,#mermaid-svg-2qRVdlVYKqkHfogS .node .label text,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape .label,#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape .label{text-anchor:middle;}#mermaid-svg-2qRVdlVYKqkHfogS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .rough-node .label,#mermaid-svg-2qRVdlVYKqkHfogS .node .label,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape .label,#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape .label{text-align:center;}#mermaid-svg-2qRVdlVYKqkHfogS .node.clickable{cursor:pointer;}#mermaid-svg-2qRVdlVYKqkHfogS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS .arrowheadPath{fill:#333333;}#mermaid-svg-2qRVdlVYKqkHfogS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-2qRVdlVYKqkHfogS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-2qRVdlVYKqkHfogS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2qRVdlVYKqkHfogS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-2qRVdlVYKqkHfogS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2qRVdlVYKqkHfogS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-2qRVdlVYKqkHfogS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster text{fill:#333;}#mermaid-svg-2qRVdlVYKqkHfogS .cluster span{color:#333;}#mermaid-svg-2qRVdlVYKqkHfogS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-2qRVdlVYKqkHfogS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-2qRVdlVYKqkHfogS rect.text{fill:none;stroke-width:0;}#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape p,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-2qRVdlVYKqkHfogS .icon-shape .label rect,#mermaid-svg-2qRVdlVYKqkHfogS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-2qRVdlVYKqkHfogS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-2qRVdlVYKqkHfogS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-2qRVdlVYKqkHfogS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 不在了 / 反复重启
有
无
有
无,退出码非 0
还在但异常
r 列大,us+sy 高
wa 高,b 列大
si/so 持续非 0
整机不忙
CLOSE-WAIT 堆积 / 大量 D 状态线程
只是部分请求慢
故障出现(告警 / 反馈 / 巡检)
§1 信息采集快照
uptime · free · df · dmesg · journalctl
进程还在吗?
ps aux · systemctl status
journalctl -k 命中
Out of memory?
§3.3 OOM 专线
dmesg / journalctl -k 有
segfault / signal 记录?
§3.1 崩溃专线
§3.5 启动失败专线
整机忙吗?
uptime + vmstat 1
CPU 瓶颈 → §3.4
IO 瓶颈 → §3.4
内存压力 → §3.3
进程活着但不干活?
§3.2 挂起专线
§3.4 性能劣化专线
决策树使用说明:
- 从树根往下走,每一步只用一条命令做判断,不要跳步。
- 走到叶子节点后进入 §3 对应专线;专线内仍然遵循「现象 → 工具 → 命令 → 判断依据」的节奏。
- 多个现象并存时(例如又慢又重启),先查"进程不在"这条线------崩溃/OOM 是根因,慢只是后果。
3. 五类问题专项排查步骤
3.1 进程崩溃(Crash)
现象
systemctl status显示Main process exited, code=killed, status=11/SEGV(或 6/ABRT)- 服务反复
activating(auto-restart),重启计数持续增长 - 监控上进程存活曲线呈锯齿状;客户端看到连接被重置
工具 :journalctl、dmesg、coredumpctl、gdb
排查流程图:
#mermaid-svg-ptiVJZFwARh8xxht{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ptiVJZFwARh8xxht .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ptiVJZFwARh8xxht .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ptiVJZFwARh8xxht .error-icon{fill:#552222;}#mermaid-svg-ptiVJZFwARh8xxht .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ptiVJZFwARh8xxht .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ptiVJZFwARh8xxht .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ptiVJZFwARh8xxht .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ptiVJZFwARh8xxht .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ptiVJZFwARh8xxht .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ptiVJZFwARh8xxht .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ptiVJZFwARh8xxht .marker.cross{stroke:#333333;}#mermaid-svg-ptiVJZFwARh8xxht svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ptiVJZFwARh8xxht p{margin:0;}#mermaid-svg-ptiVJZFwARh8xxht .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster-label text{fill:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster-label span{color:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster-label span p{background-color:transparent;}#mermaid-svg-ptiVJZFwARh8xxht .label text,#mermaid-svg-ptiVJZFwARh8xxht span{fill:#333;color:#333;}#mermaid-svg-ptiVJZFwARh8xxht .node rect,#mermaid-svg-ptiVJZFwARh8xxht .node circle,#mermaid-svg-ptiVJZFwARh8xxht .node ellipse,#mermaid-svg-ptiVJZFwARh8xxht .node polygon,#mermaid-svg-ptiVJZFwARh8xxht .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .rough-node .label text,#mermaid-svg-ptiVJZFwARh8xxht .node .label text,#mermaid-svg-ptiVJZFwARh8xxht .image-shape .label,#mermaid-svg-ptiVJZFwARh8xxht .icon-shape .label{text-anchor:middle;}#mermaid-svg-ptiVJZFwARh8xxht .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .rough-node .label,#mermaid-svg-ptiVJZFwARh8xxht .node .label,#mermaid-svg-ptiVJZFwARh8xxht .image-shape .label,#mermaid-svg-ptiVJZFwARh8xxht .icon-shape .label{text-align:center;}#mermaid-svg-ptiVJZFwARh8xxht .node.clickable{cursor:pointer;}#mermaid-svg-ptiVJZFwARh8xxht .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ptiVJZFwARh8xxht .arrowheadPath{fill:#333333;}#mermaid-svg-ptiVJZFwARh8xxht .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ptiVJZFwARh8xxht .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ptiVJZFwARh8xxht .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptiVJZFwARh8xxht .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ptiVJZFwARh8xxht .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptiVJZFwARh8xxht .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ptiVJZFwARh8xxht .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ptiVJZFwARh8xxht .cluster text{fill:#333;}#mermaid-svg-ptiVJZFwARh8xxht .cluster span{color:#333;}#mermaid-svg-ptiVJZFwARh8xxht div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ptiVJZFwARh8xxht .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ptiVJZFwARh8xxht rect.text{fill:none;stroke-width:0;}#mermaid-svg-ptiVJZFwARh8xxht .icon-shape,#mermaid-svg-ptiVJZFwARh8xxht .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ptiVJZFwARh8xxht .icon-shape p,#mermaid-svg-ptiVJZFwARh8xxht .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ptiVJZFwARh8xxht .icon-shape .label rect,#mermaid-svg-ptiVJZFwARh8xxht .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ptiVJZFwARh8xxht .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ptiVJZFwARh8xxht .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ptiVJZFwARh8xxht :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有
没有
业务代码
第三方库
只有地址没有符号
进程消失 / 反复重启
systemctl status
确认 code=killed 与信号编号
journalctl -k 找 segfault 行
确认崩溃模块
coredumpctl list
有 core dump 吗?
coredumpctl gdb 加载 core
bt / bt full 看调用栈
开启 core 生成:
ulimit -c unlimited
确认 core_pattern 后复现
栈顶在哪?
修代码:野指针 / 越界 / 断言失败
查该库已知 bug
升级版本或校验输入
安装 dbgsym 调试符号
再重新分析
排查命令
bash
systemctl status <svc> -l --no-pager
看什么 :
Main PID的退出方式------code=killed, status=11/SEGV是段错误,status=6/ABRT是程序自己 abort;以及日志最后几行的遗言。
bash
journalctl -u <svc> -n 200 --no-pager
看什么 :崩溃前最后的输出:异常栈、断言信息(
Assertion failed)、double free、corrupted size等字样。
bash
journalctl -k -b | grep -iE 'segfault|oops|panic|traps:'
看什么 :
segfault at <地址> ip <指令地址> ... in <模块名>------崩溃发生在哪个二进制或 .so 里;error标志位(4=用户态读访问缺页)。
bash
coredumpctl list
看什么 :有没有对应时间的 core dump 记录;
TIME和PID是否与事故对得上。
bash
coredumpctl info <PID或程序名>
看什么:崩溃信号编号:SIGSEGV=11 / SIGABRT=6 / SIGBUS=7 / SIGFPE=8。
bash
coredumpctl gdb <程序名>
看什么:自动加载 core 文件进入 gdb,接下来在 gdb 里看调用栈。
gdb 内部命令:
gdb
bt
bt full
info sharedlibrary
看什么 :
bt栈顶函数是崩在哪------业务代码还是第三方库;bt full看局部变量是否为野值;info sharedlibrary确认 so 是否缺符号(缺符号时栈里只有地址没有函数名)。
判断依据
| 信号 | 常见原因 | 下一步 |
|---|---|---|
| SIGSEGV (11) | 野指针、越界读写、栈溢出 | 看 bt 栈顶 + 变量值 |
| SIGABRT (6) | 程序主动 abort:断言失败、glibc 检测到堆损坏(double free) | 重点查内存操作,开 AddressSanitizer 复现 |
| SIGBUS (7) | 内存对齐错误、访问被截断的 mmap 文件 | 查磁盘是否写满导致 mmap 文件被截断 |
| SIGFPE (8) | 除零 | 看栈顶除法的除数来源 |
core 文件没生成时
bash
ulimit -c unlimited
看什么:当前 shell 允许生成的 core 大小上限,为 0 表示被限制。
bash
cat /proc/sys/kernel/core_pattern
看什么 :core 的去向。Ubuntu 默认以
|/usr/lib/systemd/systemd-coredump ...或|/usr/share/apport/apport ...形式被接管,所以用coredumpctl查,而不是去进程工作目录找文件。
需要临时把 core 落成文件时(root):
bash
echo '/tmp/core.%e.%p' > /proc/sys/kernel/core_pattern
看什么 :复现后 /tmp 下出现
core.<程序名>.<pid>,用gdb <程序> /tmp/core.xxx分析;查完记得改回原值。
提示 :gdb 里全是十六进制地址没有函数名 → 安装调试符号:apt install <包名>-dbgsym(或 <包名>-dbg)。
3.2 进程挂起 / 无响应(Hang)
现象
- 进程在、端口在,但请求全部超时;重启后短暂恢复
- load 缓慢上涨(请求堆积),但 CPU 利用率不高
ss -tn里大量CLOSE-WAIT,或Recv-Q积压不降
工具 :ps、ss、strace、gdb、/proc/<PID>/stack
排查流程图:
#mermaid-svg-Kyf7goyCs12oVZDM{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Kyf7goyCs12oVZDM .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Kyf7goyCs12oVZDM .error-icon{fill:#552222;}#mermaid-svg-Kyf7goyCs12oVZDM .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Kyf7goyCs12oVZDM .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Kyf7goyCs12oVZDM .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM .marker.cross{stroke:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Kyf7goyCs12oVZDM p{margin:0;}#mermaid-svg-Kyf7goyCs12oVZDM .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster-label text{fill:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster-label span{color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster-label span p{background-color:transparent;}#mermaid-svg-Kyf7goyCs12oVZDM .label text,#mermaid-svg-Kyf7goyCs12oVZDM span{fill:#333;color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .node rect,#mermaid-svg-Kyf7goyCs12oVZDM .node circle,#mermaid-svg-Kyf7goyCs12oVZDM .node ellipse,#mermaid-svg-Kyf7goyCs12oVZDM .node polygon,#mermaid-svg-Kyf7goyCs12oVZDM .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .rough-node .label text,#mermaid-svg-Kyf7goyCs12oVZDM .node .label text,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape .label,#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape .label{text-anchor:middle;}#mermaid-svg-Kyf7goyCs12oVZDM .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .rough-node .label,#mermaid-svg-Kyf7goyCs12oVZDM .node .label,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape .label,#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape .label{text-align:center;}#mermaid-svg-Kyf7goyCs12oVZDM .node.clickable{cursor:pointer;}#mermaid-svg-Kyf7goyCs12oVZDM .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM .arrowheadPath{fill:#333333;}#mermaid-svg-Kyf7goyCs12oVZDM .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Kyf7goyCs12oVZDM .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Kyf7goyCs12oVZDM .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kyf7goyCs12oVZDM .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Kyf7goyCs12oVZDM .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kyf7goyCs12oVZDM .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Kyf7goyCs12oVZDM .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster text{fill:#333;}#mermaid-svg-Kyf7goyCs12oVZDM .cluster span{color:#333;}#mermaid-svg-Kyf7goyCs12oVZDM div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Kyf7goyCs12oVZDM .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Kyf7goyCs12oVZDM rect.text{fill:none;stroke-width:0;}#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape p,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Kyf7goyCs12oVZDM .icon-shape .label rect,#mermaid-svg-Kyf7goyCs12oVZDM .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Kyf7goyCs12oVZDM .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Kyf7goyCs12oVZDM .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Kyf7goyCs12oVZDM :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 大量 D 状态
io_schedule
nfs_ 相关
单线程 100% CPU
所有线程接近 0%
futex
read socket / pipe
进程在、端口在,请求超时
ps -o stat / top -H -p
看线程状态与每线程 CPU
线程状态形态?
cat /proc/PID/stack
看内核阻塞点
栈里关键字?
磁盘 / 文件系统卡住
转 §3.4 IO 方向
NFS 挂载问题
strace 几乎无输出 = 用户态死循环
用 TID 在 gdb 中定位栈
strace -p -tt -T
看停在哪条调用
停在哪?
锁竞争 / 死锁
gdb: thread apply all bt
等上游数据
排查对端服务
排查命令
bash
ps -o pid,stat,wchan:32,cmd -p <PID>
看什么 :
STAT列------D表示不可中断睡眠(通常在等 IO),S是可中断睡眠;WCHAN是阻塞点的内核函数名(如io_schedule、pipe_wait)。
bash
cat /proc/<PID>/stack
看什么 :内核态调用栈:出现
io_schedule→ 在等磁盘;nfs_*→ 卡在 NFS;blk_mq_*→ 块设备层排队。
bash
ss -tnp | grep <PID>
看什么 :大量
CLOSE-WAIT= 应用没有关闭连接(代码问题);Recv-Q大 = 数据读不走;Send-Q大 = 对端不收或网络拥塞。
bash
top -H -p <PID>
看什么 :线程级 CPU------单线程 100% 多为死循环/自旋锁;全部线程接近 0% 说明在等锁或等外部输入。记下可疑线程的 TID(
top -H显示的 PID 列即 TID)。
bash
strace -p <PID> -tt -T -f
看什么 :卡在哪条系统调用上:停在
futex(...)= 等锁/条件变量;停在read(fd)= 等上游或文件;-tt显示微秒时间戳,-T显示每条调用耗时。观察完尽快 Ctrl+C 退出,strace 本身有明显开销。
bash
timeout 10 strace -p <PID> -c
看什么 :10 秒系统调用统计汇总------
futex次数和耗时占比高 → 锁竞争;read/write为主 → 正常 IO 路径,转性能方向。
bash
gdb -p <PID>
gdb
thread apply all bt
detach
quit
看什么 :所有线程的用户态调用栈------多个线程停在同一把锁(
pthread_mutex_lock/futex)→ 热点锁或死锁;单线程停在某个业务函数 → 逻辑问题(死循环、无限重试)。gdb attach 会短暂冻结进程,生产上用完立即 detach。
判断依据
| 证据组合 | 结论 |
|---|---|
strace 停 futex + 线程全 0% CPU + 栈都卡同一把锁 |
应用内死锁 / 长临界区 → 查加锁顺序 |
线程 D 状态 + /proc/<PID>/stack 有 io_schedule |
磁盘 / 文件系统把进程拖住了 → 转 §3.4 IO 方向,并查磁盘健康 |
| 单线程 100% CPU + strace 几乎无输出 | 用户态死循环 → 用 TID 在 gdb 里定位该线程的栈 |
大量 CLOSE-WAIT + 应用线程都在 read socket |
上游不返回数据 → 问题在对端,查上游服务 |
3.3 OOM(内存耗尽被杀)
现象
- 进程突然消失,应用自己的日志里没有任何崩溃栈
- 系统日志出现
Out of memory: Killed process - 机器先整体变慢(swap 疯狂换页),随后某进程被杀
工具 :journalctl -k、free、ps、pmap、slabtop、systemd-cgtop
排查流程图:
#mermaid-svg-AR1uvkLbQVPDdkkj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-AR1uvkLbQVPDdkkj .error-icon{fill:#552222;}#mermaid-svg-AR1uvkLbQVPDdkkj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-AR1uvkLbQVPDdkkj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-AR1uvkLbQVPDdkkj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj .marker.cross{stroke:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-AR1uvkLbQVPDdkkj p{margin:0;}#mermaid-svg-AR1uvkLbQVPDdkkj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster-label text{fill:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster-label span{color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster-label span p{background-color:transparent;}#mermaid-svg-AR1uvkLbQVPDdkkj .label text,#mermaid-svg-AR1uvkLbQVPDdkkj span{fill:#333;color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .node rect,#mermaid-svg-AR1uvkLbQVPDdkkj .node circle,#mermaid-svg-AR1uvkLbQVPDdkkj .node ellipse,#mermaid-svg-AR1uvkLbQVPDdkkj .node polygon,#mermaid-svg-AR1uvkLbQVPDdkkj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .rough-node .label text,#mermaid-svg-AR1uvkLbQVPDdkkj .node .label text,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape .label,#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape .label{text-anchor:middle;}#mermaid-svg-AR1uvkLbQVPDdkkj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .rough-node .label,#mermaid-svg-AR1uvkLbQVPDdkkj .node .label,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape .label,#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape .label{text-align:center;}#mermaid-svg-AR1uvkLbQVPDdkkj .node.clickable{cursor:pointer;}#mermaid-svg-AR1uvkLbQVPDdkkj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj .arrowheadPath{fill:#333333;}#mermaid-svg-AR1uvkLbQVPDdkkj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-AR1uvkLbQVPDdkkj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-AR1uvkLbQVPDdkkj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AR1uvkLbQVPDdkkj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-AR1uvkLbQVPDdkkj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AR1uvkLbQVPDdkkj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster text{fill:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj .cluster span{color:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-AR1uvkLbQVPDdkkj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-AR1uvkLbQVPDdkkj rect.text{fill:none;stroke-width:0;}#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape p,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-AR1uvkLbQVPDdkkj .icon-shape .label rect,#mermaid-svg-AR1uvkLbQVPDdkkj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-AR1uvkLbQVPDdkkj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-AR1uvkLbQVPDdkkj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-AR1uvkLbQVPDdkkj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 没有
有
是
否
逐日上涨不回落
突发尖峰
进程突然消失,无日志
journalctl -k
搜 Out of memory / Killed process
有 OOM kill 记录?
不是 OOM
转 §3.1 崩溃 / §3.5 启动失败
含 Memory cgroup
out of memory?
打到服务自身 cgroup 限额
systemctl show 查 MemoryMax
整机内存不足
记录被杀进程 anon-rss
RSS 增长形态?
慢性泄漏
pmap / heap dump 定位增长段
突发大分配
大查询 / 批量任务 / 缓存击穿
排查命令
第一步:确认是 OOM、确认杀的是谁。
bash
journalctl -k | grep -iE 'oom|killed process' | tail -20
看什么 :
Out of memory: Killed process <pid> (<名字>) total-vm:xxxkB, anon-rss:xxxkB------被杀进程实际占用的物理内存(anon-rss)是多少。
bash
journalctl -k | grep -iB 20 'killed process' | grep -i 'memory cgroup out of memory'
看什么 :是否出现
Memory cgroup out of memory------有,说明是该服务自己的 cgroup 内存上限被打爆(unit 里配了MemoryMax=),不是整机内存不足,排查范围直接缩小到这一个服务。
第二步:当前内存画像。
bash
free -h
看什么 :盯
available(真正可用),不是free;buff/cache高通常是正常文件缓存,可回收,不算泄漏。
bash
grep -E 'MemAvailable|Slab|SReclaimable|PageTables|KernelStack' /proc/meminfo
看什么 :
Slab是否异常大(内核对象泄漏);PageTables是否异常大(进程虚拟地址空间过大,常见于配置过大的 JVM 堆)。
bash
ps aux --sort=-rss | head -15
看什么:RSS 第一名是谁,占整机内存的比例。
bash
systemd-cgtop -m
看什么:按 cgroup(≈按服务)排序的内存占用榜,找出哪个单元吃得最多。
第三步:追踪泄漏。
bash
grep -E 'VmRSS|VmHWM|RssAnon|Threads' /proc/<PID>/status
看什么 :每隔几分钟采样一次
VmRSS------只涨不落 = 疑似泄漏;VmHWM是历史峰值。
bash
pmap -x <PID> | tail -1
看什么:进程 RSS 总量;间隔运行两次做差,看增量在哪个映射段(配合下条命令)。
bash
pmap -x <PID> | sort -k3 -n -r | head -15
看什么 :RSS 最大的匿名映射段------
[heap]持续增长 = 应用内存泄漏;出现大量 64MB 左右的匿名段增长 = glibc malloc arena 碎片化,可试MALLOC_ARENA_MAX=2。
bash
slabtop -o | head -15
看什么 :整机 SLAB 大时,哪类内核对象最多------
dentry_cache/inode_cache暴涨通常意味着某个进程在遍历/创建海量小文件。
判断依据
| 证据 | 结论 |
|---|---|
| 被杀进程 anon-rss ≈ 整机内存 | 进程真吃得多:泄漏或容量规划错误 |
日志含 Memory cgroup out of memory |
是 systemd 限额被打爆,查 `systemctl show |
| RSS 缓慢爬升数天后被杀 | 典型慢性泄漏 → 应用层 heap dump / pmap 定位 |
| 内存尖峰后立刻被杀 | 突发大对象:大查询、大批量任务、缓存击穿 |
| free 不高但仍 OOM | 查 cgroup 限制;或 PageTables/SLAB 等不可回收部分占比 |
缓解手段(不是根治,买时间用):
bash
echo -500 > /proc/<PID>/oom_score_adj
看什么 :oom_score_adj 范围 -1000~1000,越小越不容易被杀;systemd 服务请在 unit 里配
OOMScoreAdjust=,重启不丢失。
3.4 性能劣化
现象
- 接口 P99 延迟上涨、吞吐下降
- 周期性变慢(每天固定时间、每周某天)
- 用户反馈"比以前卡",但没报错
工具 :uptime、vmstat、iostat、pidstat、perf、top -H
排查流程图:
#mermaid-svg-49SYvazk7NFf7Sw6{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-49SYvazk7NFf7Sw6 .error-icon{fill:#552222;}#mermaid-svg-49SYvazk7NFf7Sw6 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-49SYvazk7NFf7Sw6 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-49SYvazk7NFf7Sw6 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 .marker.cross{stroke:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-49SYvazk7NFf7Sw6 p{margin:0;}#mermaid-svg-49SYvazk7NFf7Sw6 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster-label text{fill:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster-label span{color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster-label span p{background-color:transparent;}#mermaid-svg-49SYvazk7NFf7Sw6 .label text,#mermaid-svg-49SYvazk7NFf7Sw6 span{fill:#333;color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .node rect,#mermaid-svg-49SYvazk7NFf7Sw6 .node circle,#mermaid-svg-49SYvazk7NFf7Sw6 .node ellipse,#mermaid-svg-49SYvazk7NFf7Sw6 .node polygon,#mermaid-svg-49SYvazk7NFf7Sw6 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .rough-node .label text,#mermaid-svg-49SYvazk7NFf7Sw6 .node .label text,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape .label,#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape .label{text-anchor:middle;}#mermaid-svg-49SYvazk7NFf7Sw6 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .rough-node .label,#mermaid-svg-49SYvazk7NFf7Sw6 .node .label,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape .label,#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape .label{text-align:center;}#mermaid-svg-49SYvazk7NFf7Sw6 .node.clickable{cursor:pointer;}#mermaid-svg-49SYvazk7NFf7Sw6 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 .arrowheadPath{fill:#333333;}#mermaid-svg-49SYvazk7NFf7Sw6 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-49SYvazk7NFf7Sw6 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-49SYvazk7NFf7Sw6 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-49SYvazk7NFf7Sw6 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-49SYvazk7NFf7Sw6 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-49SYvazk7NFf7Sw6 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster text{fill:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 .cluster span{color:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-49SYvazk7NFf7Sw6 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-49SYvazk7NFf7Sw6 rect.text{fill:none;stroke-width:0;}#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape p,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-49SYvazk7NFf7Sw6 .icon-shape .label rect,#mermaid-svg-49SYvazk7NFf7Sw6 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-49SYvazk7NFf7Sw6 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-49SYvazk7NFf7Sw6 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-49SYvazk7NFf7Sw6 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} r 大于核数,us 高
sy 高,cs 暴涨
wa 高,b 列大于 0
si/so 非 0
st 高
指标全正常
延迟升高 / 吞吐下降
vmstat 1 象限定界
哪个指标异常?
用户态吃 CPU
pidstat -u 找进程
perf top / perf record -g
定位热点函数
内核态 / 上下文切换风暴
pidstat -w,查线程数与锁
IO 瓶颈
iostat -xz 找饱和盘
pidstat -d 找进程
查慢 SQL / 日志狂写 / 盘老化
正在换页
转 §3.3 内存排查
宿主机偷 CPU
云 VM 邻居噪音:迁移 / 升配
非资源瓶颈
锁竞争 / 下游依赖慢 / 网络
排查命令
第一步:定位瓶颈象限。
bash
uptime
看什么:load 三个数(1/5/15 分钟)的相对大小------1 分钟远高于 15 分钟 = 正在恶化;反之路过高峰在恢复。
bash
vmstat 1 10
看什么:
r(运行队列)持续 > CPU 核数 → CPU 不够b(阻塞进程)持续 > 0 → 在等 IOus高 → 用户态(应用代码)耗 CPU;sy高 → 内核态(系统调用/上下文切换)耗 CPUwa高 → CPU 空转等 IO;st高 → 宿主机偷走了 CPU(云虚拟机常见)si/so非 0 → 正在换页,内存已捉襟见肘cs(上下文切换)暴涨 → 线程过多或锁竞争激烈
第二步:磁盘维度。
bash
iostat -xz 1 5
看什么 :
%util接近 100 = 该盘已饱和;await明显高于平时(SSD 正常 <1ms,HDD <15ms)= 单次 IO 慢;aqu-sz大 = 请求在排队。
第三步:定位到进程。
bash
pidstat -u 1 5
看什么 :哪个进程的
%CPU最高。
bash
pidstat -d 1 5
看什么 :哪个进程的
kB_wr/s、kB_rd/s最大------把 §第一步的 IO 饱和和具体进程对上号。
bash
pidstat -w 1 5
看什么 :
nvcsw/s(主动切换)高 = 频繁等锁/等 IO;csw/s(被动切换)高 = CPU 争抢,时间片被强制让出。
bash
top -H -p <PID>
看什么:CPU 消耗集中在哪个线程,记下 TID,供 perf / gdb 精确定位。
第四步:CPU 热点函数。
bash
perf top
看什么 :实时函数热点榜------业务函数占比高就是算法问题;
__libc_malloc、__GI___libc_free占比高提示频繁分配;内核函数(如_raw_spin_lock)占比高提示内核态竞争。
bash
perf record -F 99 -g -p <PID> -- sleep 30 && perf report
看什么 :30 秒采样后的调用树,占比最高的调用链就是优化对象。若报权限错误:
sudo sysctl kernel.perf_event_paranoid=1;虚拟机里若不可用,退回pidstat + top -H组合。
判断依据
| 观测组合 | 瓶颈判定 | 下一步 |
|---|---|---|
us 高 + r 大 |
应用吃 CPU | pidstat 找进程 → perf 找函数 |
sy 高 + cs 大 |
上下文切换 / 系统调用风暴 | pidstat -w 找进程;查线程数是否失控 |
wa 高 + b>0 + %util≈100 |
磁盘 IO 饱和 | iostat 定盘 → pidstat -d 定进程 → 查慢 SQL/日志狂写 |
st 高 |
宿主机超卖 | 云 VM "邻居噪音",考虑迁移/升配 |
si/so 非 0 |
内存不足引发换页 | 转 §3.3 内存排查 |
| 各项资源都不高但延迟高 | 锁竞争 / 外部依赖慢 | strace 看 futex;查下游服务与网络 |
3.5 服务启动失败
现象
systemctl start <svc>直接报错,或状态在activating(auto-restart)循环systemctl status显示code=exited, status=N
工具 :systemctl、journalctl、systemd-analyze、strace
排查流程图:
#mermaid-svg-Jrcs4mnyRpIcbgXX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Jrcs4mnyRpIcbgXX .error-icon{fill:#552222;}#mermaid-svg-Jrcs4mnyRpIcbgXX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Jrcs4mnyRpIcbgXX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .marker.cross{stroke:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Jrcs4mnyRpIcbgXX p{margin:0;}#mermaid-svg-Jrcs4mnyRpIcbgXX .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster-label text{fill:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster-label span{color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster-label span p{background-color:transparent;}#mermaid-svg-Jrcs4mnyRpIcbgXX .label text,#mermaid-svg-Jrcs4mnyRpIcbgXX span{fill:#333;color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node rect,#mermaid-svg-Jrcs4mnyRpIcbgXX .node circle,#mermaid-svg-Jrcs4mnyRpIcbgXX .node ellipse,#mermaid-svg-Jrcs4mnyRpIcbgXX .node polygon,#mermaid-svg-Jrcs4mnyRpIcbgXX .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .rough-node .label text,#mermaid-svg-Jrcs4mnyRpIcbgXX .node .label text,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape .label{text-anchor:middle;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .rough-node .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .node .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape .label,#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape .label{text-align:center;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node.clickable{cursor:pointer;}#mermaid-svg-Jrcs4mnyRpIcbgXX .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .arrowheadPath{fill:#333333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-Jrcs4mnyRpIcbgXX .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Jrcs4mnyRpIcbgXX .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster text{fill:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX .cluster span{color:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-Jrcs4mnyRpIcbgXX .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-Jrcs4mnyRpIcbgXX rect.text{fill:none;stroke-width:0;}#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape p,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-Jrcs4mnyRpIcbgXX .icon-shape .label rect,#mermaid-svg-Jrcs4mnyRpIcbgXX .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-Jrcs4mnyRpIcbgXX .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-Jrcs4mnyRpIcbgXX .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-Jrcs4mnyRpIcbgXX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1 / 2
126 / 127
200 / 203
216 / 217
systemctl start 失败 /
auto-restart 循环
systemctl status -l
记退出码与日志末尾几行
退出码归类?
应用自身报错
journalctl -u -o cat 看栈
无执行权限 / 找不到命令
查 ExecStart 路径与权限
WorkingDirectory / 可执行文件问题
systemd-analyze verify
User / Group 不存在
修正 unit 文件
五类常见根因逐一排查
端口冲突
ss -lntp 看谁占用
配置错误
手动运行验证
权限不足
sudo -u 复现
文件缺失
strace openat 看 ENOENT
依赖未就绪
查 After= 顺序
排查命令
bash
systemctl status <svc> -l --no-pager
看什么 :三件事------①
Active状态;②Main PID ... code=exited, status=N退出码;③ 末尾几行日志,往往直接写着原因。
bash
journalctl -u <svc> -b --no-pager | tail -100
看什么 :本次开机该服务的全部日志------配置解析错误(
parse error、invalid)、端口冲突(Address already in use)、权限拒绝(Permission denied)、依赖连不上(Connection refused)。
bash
journalctl -u <svc> -o cat
看什么 :
-o cat去掉 systemd 前缀,只留应用原始输出,业务异常栈更容易看出来。
退出码速查
| status | 含义 | 排查方向 |
|---|---|---|
| 1 / 2 | 应用自身报错退出 | 看应用日志 |
| 126 | 文件存在但不可执行 | 文件权限(chmod +x) |
| 127 | 命令或依赖文件找不到 | ExecStart 路径、动态库缺失 |
| 200 (CHDIR) | 工作目录不可进入 | WorkingDirectory= 指向的目录不存在 |
| 203 (EXEC) | 无法执行 ExecStart | 路径写错、无权限、脚本 shebang 解释器缺失 |
| 216 / 217 | Group / User 不存在 | unit 里 User=、Group= 写错 |
单元文件检查:
bash
systemd-analyze verify /etc/systemd/system/<svc>.service
看什么:unit 语法错误和告警,例如 ExecStart 路径不存在。
bash
systemctl cat <svc>
看什么 :完整生效配置(含 drop-in 覆盖):
User、WorkingDirectory、ExecStart、EnvironmentFile、LimitNOFILE。
bash
cat $(systemctl show <svc> -p EnvironmentFile --value)
看什么 :环境文件是否存在、格式是否为
KEY=VALUE(不能带export、不能有行内注释展开)。
端口占用:
bash
ss -lntp | grep <端口>
看什么:端口被哪个进程占着------常见于旧进程没退干净,或两个服务配了同一端口。
以运行用户身份手动复现:
bash
sudo -u <服务运行用户> <ExecStart 原始命令>
看什么:报什么错。root 手动跑能起来、服务用户跑不起来 = 权限问题;这一步能复现 80% 的启动失败。
日志不足时追踪系统调用:
bash
strace -f -e trace=openat,connect,bind -o /tmp/<svc>.trace <ExecStart 原始命令>
看什么 :哪个文件打开失败(
= -1 ENOENT路径不存在 /EACCES无权限)、哪个connect/bind失败。
判断依据:退出码定大方向,日志定具体行。90% 的启动失败归为五类------配置错误、端口冲突、权限不足、路径/文件缺失、依赖服务未就绪。改过 unit 文件后务必:
bash
systemctl daemon-reload && systemctl restart <svc>
看什么:不先 daemon-reload,重启用的还是旧配置,会造成"改了没生效"的错觉。
4. 常用工具速查表
标注「自带」的为 Ubuntu 服务器默认安装;其余一条命令装齐:
bash
sudo apt update && sudo apt install -y gdb strace lsof sysstat iotop tcpdump
sudo apt install -y linux-tools-common linux-tools-generic "linux-tools-$(uname -r)"
看什么 :第二条装 perf,必须和当前内核版本匹配,装完用
perf --version验证能运行。
| 工具 | 来源 | 典型场景 | 常用命令示例 | 重点看什么 |
|---|---|---|---|---|
top |
自带 (procps) | 总览 CPU / load / 热点线程 | top、top -H -p <PID> |
load、us/sy/wa、哪个线程吃 CPU |
ps |
自带 (procps) | 进程快照与进程树 | ps auxf、`ps -eLf |
wc -l` |
vmstat |
自带 (procps) | CPU/内存/切换趋势 | vmstat 1 |
r/b、us/sy/wa/st、si/so、cs |
iostat |
sysstat | 磁盘饱和度 | iostat -xz 1 |
%util、await、aqu-sz |
pidstat |
sysstat | 按进程定位 CPU/IO/切换 | pidstat -u 1 / -d 1 / -w 1 |
哪个进程在耗资源 |
sar |
sysstat | 回看历史指标 | sar -u -f /var/log/sysstat/sa15 |
故障发生时刻的指标 |
perf |
linux-tools | CPU 热点与调用栈 | perf top、perf record -g |
热点函数、调用链占比 |
strace |
需安装 | 系统调用跟踪 | strace -p <PID> -tt -T -f |
卡在哪个调用、错误码(ENOENT/EACCES) |
gdb |
需安装 | 调用栈 / 死锁 / core 分析 | gdb -p <PID> → thread apply all bt |
线程栈、崩溃 backtrace |
lsof |
需安装 | 文件 / 连接 / 端口占用 | lsof -p <PID>、lsof -i :80 |
fd 数量与类型、谁占着端口 |
ss |
自带 (iproute2) | 网络连接状态 | ss -lntp、ss -s、ss -tnp |
监听是否齐全、状态分布、队列积压 |
dmesg |
自带 | 内核消息 | `dmesg -T | tail、dmesg -w` |
journalctl |
自带 (systemd) | 系统与服务的统一日志 | journalctl -u <svc> -b、journalctl -k |
错误前后文、时间对齐 |
coredumpctl |
自带 (systemd) | core dump 管理 | coredumpctl list、coredumpctl gdb <名> |
有无 core、直接加载分析 |
pmap |
自带 (procps) | 进程内存映射 | pmap -x <PID> |
哪段映射的 RSS 在涨 |
slabtop |
自带 (procps) | 内核 SLAB 缓存 | slabtop -o |
哪类内核对象占内存 |
systemd-cgtop |
自带 (systemd) | 按服务看资源占用 | systemd-cgtop -m |
各 cgroup 内存/CPU 排行 |
systemd-analyze |
自带 (systemd) | 开机与 unit 诊断 | systemd-analyze blame、verify |
开机慢在哪、unit 语法 |
iotop |
需安装 | 实时看谁在读写磁盘 | iotop -oP |
IO 进程排行(需要 root) |
tcpdump |
需安装 | 抓包 | tcpdump -i any port 80 -w /tmp/x.pcap |
握手是否完成、重传、RST |
工具使用分寸
strace -p让目标进程明显变慢,生产环境只做短时间观察。gdb -pattach 期间进程被冻结,对延迟敏感的服务优先走 core dump 离线分析。perf record采样开销低(1% 以内),生产可用,但查完删除 perf.data。
5. 三个真实案例复盘
案例一:凌晨随机崩溃,应用日志干干净净(崩溃类)
现象:某图片处理服务(C++ 核心 + 上层封装)凌晨 03:12 被监控发现连续重启 2 次,期间部分任务处理失败。翻遍应用日志只有正常业务输出,没有任何异常栈。
排查过程:
bash
systemctl status imgsvc
看什么 :
Main process exited, code=killed, status=11/SEGV------进程不是自己退的,是被 SIGSEGV 杀掉的,应用日志里没有栈很正常(段错误不走异常处理)。
bash
journalctl -k -b | grep -i segfault
看什么 :
segfault at 18 ip 00007f2c... error 4 in libjpeg.so.8.2.2------崩溃点在 libjpeg 内部,不是业务代码直接崩。
bash
coredumpctl list imgsvc
看什么:03:12 有 core 记录,可离线分析。
bash
coredumpctl gdb imgsvc
gdb
bt
看什么 :栈顶在 libjpeg 的
decompress_smooth_data,上一帧是业务的图片解码入口------解码某张特定图片时崩溃。
根因 :上游偶发传入截断的 JPEG 文件;所用 libjpeg 旧版本对这类畸形文件有已知崩溃缺陷。
处理 :升级 libjpeg;业务侧在解码前做文件完整性校验,畸形文件直接拒绝并告警。
复盘要点 :应用日志没有错误 ≠ 应用没出错------信号类崩溃不会走日志路径;dmesg 和 coredumpctl 才是第一现场;native 库的坑通常要特定输入才触发,复现靠留下问题文件。
案例二:超时风暴,但 CPU 很空闲(挂起 / 性能类)
现象:某工作日下午 API 网关大量超时,监控显示 CPU 利用率仅 15% 左右,但 load 从 4 一路涨到 30+。典型的"机器不忙但就是卡"。
排查过程:
bash
vmstat 1
看什么 :
r=2 b=41 ... wa=46------运行队列很短,但有 41 个进程堵在不可中断睡眠,CPU 近一半时间在等 IO。CPU 空闲 + b 列大 = 瓶颈在磁盘。
bash
iostat -xz 1
看什么 :
sda %util=100 await=860------唯一的数据盘已完全饱和,单次 IO 平均等 860ms。
bash
pidstat -d 1
看什么 :
postgres kB_wr/s=41200------数据库进程在疯狂写盘。但 PostgreSQL 平时写入只有几百 kB/s。
bash
ss -tn | grep :5432 | wc -l
看什么:数据库连接数从平时 80 涨到 240------请求处理被 IO 拖慢,连接池被占满,新请求排队超时。
继续查"谁让数据库写这么多":慢查询日志无异常,最后定位到当天新上线的一个数据导出任务在跑全表扫描 + 大批量写临时文件,和业务库共用同一块盘。
根因 :导出任务与数据库争抢同一块磁盘的 IO,数据库写入被拖垮,连接堆积引发级联超时。
处理 :立即停止导出任务,服务分钟级恢复;后续把批量任务挪到低峰时段、用 ionice -c3 降为最低 IO 优先级、备份/导出使用独立磁盘。
复盘要点 :"CPU 空闲 ≠ 系统空闲",vmstat 的 b 列和 wa 是识破假象的第一把钥匙;IO 饱和时 load 会涨但 CPU 不涨;批量任务上线必须有资源限制和时间窗口评审。
案例三:每 2~4 天被杀一次(OOM 类)
现象:某 Java 服务运行 2~4 天后进程消失,systemd 自动拉起,如此循环。业务侧感知为"每隔几天有一次几分钟的服务抖动"。
排查过程:
bash
journalctl -k | grep -i 'killed process'
看什么 :
Out of memory: Killed process 2451 (java) total-vm:8388608kB, anon-rss:7912448kB------实锤 OOM;anon-rss 约 7.9GB,而 JVM 堆只配了 4GB,说明堆外也有大量增长,或堆增长不受 -Xmx 约束的部分在吃内存。
bash
free -h
看什么 :事故前
available只剩 200MB 左右,整机 8GB 内存。
追踪趋势(连续观察两天):
bash
grep VmRSS /proc/<PID>/status
看什么:RSS 从 4.2GB 单调爬升到 7.9GB,中间没有回落------"只涨不落"就是泄漏的典型曲线,与突发大对象(尖峰)形态不同。
bash
pmap -x <PID> | sort -k3 -n -r | head
看什么:增长集中在 heap 段,指向 JVM 堆内对象泄漏,而不是 native 泄漏。
应用侧:jmap -dump 导出堆快照分析,发现一个本地会话缓存(Map<String, Session>)条目超过 200 万,只进不出。
根因 :会话缓存只添加不清理、无过期机制,堆持续增长直至整机 OOM,内核 OOM killer 选中 RSS 最大的 java 进程。
处理 :改用带容量上限 + 过期淘汰的缓存组件;同时在监控里加了该服务 RSS 的告警(阈值 6GB),保证下次泄漏在 OOM 之前被发现。
复盘要点 :OOM 排查第一步永远是 journalctl -k 确认"杀的是谁、用了多少";"周期性被杀 + RSS 缓慢爬升"是内存泄漏的教科书形态;修复泄漏之外,必须补上 RSS 监控告警,否则下次还是靠用户反馈发现。
6. 排查记录模板
每次真实排查都留一份记录,既是复盘材料,也是下次遇到同类问题的检索入口。
markdown
# 排查记录:<一句话标题,例:订单服务凌晨 OOM 重启>
## 基本信息
| 项目 | 内容 |
|------|------|
| 发生时间 | YYYY-MM-DD HH:MM |
| 发现方式 | 告警 / 用户反馈 / 巡检 |
| 影响服务 | |
| 影响范围 | |
| 严重等级 | P0-P3 |
| 处理人 | |
## 1. 现象描述
<监控上看到了什么、用户感知是什么、从何时开始、是否可复现>
## 2. 时间线
| 时间 | 事件 |
|------|------|
| HH:MM | 告警触发 / 现象出现 |
| HH:MM | 首次响应,初步判断方向 |
| HH:MM | 定位到根因 |
| HH:MM | 止血 / 恢复 |
## 3. 信息采集记录
| 命令 | 目的 | 关键输出 / 结论 |
|------|------|------------------|
| free -h | 看内存 | available 仅剩 200MB |
| | | |
## 4. 排查过程(假设 → 验证)
1. 假设 A:<...> → 用 <命令> 验证 → 排除 / 确认
2. 假设 B:<...> → 用 <命令> 验证 → 排除 / 确认
## 5. 根因
<一句话说清根因 + 机制解释>
## 6. 处置与修复
<紧急止血措施 + 长期修复方案,关联 commit / 配置变更记录>
## 7. 后续行动项
| 事项 | 负责人 | 截止时间 |
|------|--------|----------|
| 补充监控告警 | | |
| | | |
## 8. 附录
- 相关日志 / 截图 / core 文件路径:
- 相关链接(监控面板、工单、PR):
附录:常见误区与注意事项
- 不要上来就重启。重启会销毁现场(内存数据、连接状态、core 机会);除非影响面正在扩大,先跑完 §1 采集清单。
free里 buff/cache 高不是内存泄漏 ,判断标准是available。- load 高 ≠ CPU 高。D 状态(不可中断睡眠)进程同样计入 load,磁盘卡住时 load 可以非常高而 CPU 很闲。
- strace 有显著开销,生产环境 attach 只做短时观察,用完立即退出。
- gdb attach 会冻结进程,对延迟敏感的服务优先离线分析 core dump。
- perf 必须匹配内核版本 :
linux-tools-$(uname -r);虚拟机报权限错误时调低kernel.perf_event_paranoid。 - Ubuntu 的 core dump 被 systemd-coredump 接管 ,用
coredumpctl查,不要去进程目录里找 core 文件。 - 区分两种退出 :
code=killed是被信号杀(外部/OOM/段错误),code=exited是进程自己退出(看 status 码)。 - 磁盘"满"有两种 :容量满(
df -h)和 inode 满(df -i),后者更隐蔽。 - 改了 unit 文件先
systemctl daemon-reload,否则重启加载的还是旧配置。