← 返回面试目录

🚨 线上问题排查全手册

5分钟定位法 · 手把手排查 · 命令速查 · 应急止血 · 根因分析

应急响应 CPU飙高 OOM排查 网络排查 数据库 Redis/MQ

1. 应急响应流程(5分钟定位法)

线上出问题,第一反应不是查原因,而是先止血!遵循"先恢复服务,再排查根因"原则。

1接到报警
短信/电话/钉钉
2确认影响范围
多少用户?哪个功能?
3看监控大盘
CPU/内存/QPS/RT
4看最近变更
发版了?配置改了?
5缩小范围
网络?服务?存储?
6定位问题
7止血恢复
先恢复再查!
8排查根因
修复 + 复盘

止血优先原则:先恢复服务再查原因!不要在用户还挂着的时候慢慢 debug。服务恢复了,再回头分析 dump 和日志。

止血手段速查

手段适用场景操作风险
重启 进程假死、内存泄漏 kill -9 + 自动拉起 丢失现场,先 dump 再杀
回滚 新版本引入的 bug git revert + 重新部署 低风险,推荐优先尝试
限流 流量突增压垮服务 Nginx限流 / Sentinel 部分用户受影响
降级 非核心功能拖垮主流程 关闭非核心接口 功能暂时不可用
切流量 单机房故障 DNS / LB 切到备用机房 需要多机房部署

应急响应就像急诊:先止血(恢复服务)→ 再检查(定位原因)→ 最后根治(修复上线)。不要在病人还在出血的时候做全身检查!

2. CPU 飙高排查(手把手)

排查命令流程

top
找高CPU进程PID
top -Hp PID
找高CPU线程TID
printf '%x' TID
转十六进制nid
jstack PID | grep nid
定位到代码行
# Step 1: 找到占用 CPU 最高的进程
top -c
# 记下 PID,假设 PID = 12345

# Step 2: 找到该进程中占用 CPU 最高的线程
top -Hp 12345
# 记下线程 TID,假设 TID = 12350

# Step 3: 线程ID转十六进制(jstack里用的是十六进制)
printf '%x\n' 12350
# 输出: 303e

# Step 4: 查看线程堆栈,定位到具体代码
jstack 12345 | grep "0x303e" -A 30

# 输出示例:
# "pool-1-thread-5" #28 prio=5 os_prio=0 tid=0x00007f8c40123000 nid=0x303e runnable
# at com.example.service.OrderService.processOrder(OrderService.java:87)
# ↑ 这行就是罪魁祸首!

常见原因速查表

原因特征jstack表现解决方案
死循环 单个线程 CPU 100% 线程一直停在某个 while 循环 修复循环退出条件,加最大次数限制
正则回溯 CPU突增,请求含特殊字符 停留在 Pattern.matcher / Regex 换用预编译正则,避免贪婪匹配,限制输入长度
频繁 Full GC 多个GC线程CPU高 GC线程频繁出现 jstat -gcutil 确认 → 扩堆/优化对象创建/排查内存泄漏
加密/序列化操作 批量任务时CPU高 停留在加密/序列化代码 异步处理、限制批量大小、用更快的算法
线程池满 大量线程同时活跃 线程数远超预期 调优线程池参数、排查线程泄漏、拒绝策略
# 快速确认是不是 GC 导致的 CPU 高
jstat -gcutil 12345 1000 5
# 如果 FGC 列数字在持续增长 → 频繁 Full GC

# 查看 GC 日志(启动参数里加过的话)
tail -f /var/log/app/gc.log | grep "Full GC"

快速判断:top 看到高 CPU 后,先 jstat 看一眼 GC 频率。如果 FGC 一秒好几次,大概率是 GC 问题而不是代码问题。

3. 内存溢出/OOM 排查

排查流程

OOM 报错
jmap 导出堆dump
MAT 分析
找泄漏对象
# 1. 线上务必配好 OOM 自动 dump(JVM 启动参数)
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/data/logs/heapdump.hprof

# 2. 进程还活着时手动导出 dump
jmap -dump:format=b,file=/tmp/heap_$(date +%s).hprof 12345

# 3. 查看堆内存概览(不需要 dump)
jmap -histo:live 12345 | head -20
# 看哪个类的实例数最多,快速判断泄漏类型

# 4. 用 MAT (Memory Analyzer Tool) 分析 dump
# 打开 .hprof 文件 → Leak Suspects → Dominator Tree

常见 OOM 原因

原因表现MAT特征解决方案
ThreadLocal 泄漏 堆内存持续增长 ThreadLocalMap 中大量 Entry 用完 remove();线程池场景尤其注意
集合无限增长 HashMap/ArrayList 越来越大 某个 Map 实例占内存最大 加容量上限(LRU),定期清理,检查 only-add-no-remove
大对象查询 单次查询返回几万条数据 大量业务对象实例 SQL 加 LIMIT,分页查询,流式处理
Metaspace 满 java.lang.OutOfMemoryError: Metaspace 加载的类数量异常多 加大 -XX:MaxMetaspaceSize,排查动态代理/CGLIB
直接内存泄漏 堆内存正常但进程 OOM NIO DirectByteBuffer 相关 检查 ByteBuf 是否 release,调整 -XX:MaxDirectMemorySize

JVM 参数配置建议

# 生产环境推荐 JVM 参数模板
-Xms4g # 初始堆大小(与最大堆一致,避免动态扩缩)
-Xmx4g # 最大堆大小
-Xmn2g # 年轻代大小
-XX:+UseG1GC # 使用 G1 垃圾收集器
-XX:MaxGCPauseMillis=200 # 目标 GC 停顿时间
-XX:+HeapDumpOnOutOfMemoryError # OOM 时自动 dump
-XX:HeapDumpPath=/data/logs/ # dump 文件路径
-Xloggc:/data/logs/gc.log # GC 日志路径
-XX:+PrintGCDetails # 打印 GC 详情
-XX:+PrintGCDateStamps # GC 日志加时间戳
-XX:MaxMetaspaceSize=256m # 限制 Metaspace 大小

关键操作:线上 OOM 时,先 jmap -dump 导出堆快照,再重启!重启后现场就没了,没有 dump 就无法定位原因。dump 文件一般很大,注意磁盘空间。

4. 磁盘满排查

排查流程

磁盘告警
df -h
看哪个分区满
du -sh *
定位大目录
找大文件
清理或迁移
# Step 1: 查看各分区使用率
df -h

# Step 2: 进入使用率最高的分区,逐层找大目录
cd / && du -sh * | sort -rh | head -10

# Step 3: 继续进入大目录,逐层缩小范围
cd /var/log && du -sh * | sort -rh | head -10

# Step 4: 找到具体大文件
find / -type f -size +500M -exec ls -lh {} \; 2>/dev/null

# Step 5: 找已被删除但被进程占用的文件(空间未释放)
lsof | grep "deleted" | sort -k7 -rh | head -10
# 这类文件需要重启占用进程才能真正释放空间

常见占空间的东西

类型路径示例清理方法
日志文件 /var/log/app/*.log 配置 logback 滚动策略,保留 N 天;用 logrotate
Docker 镜像 /var/lib/docker/ docker system prune -a;清理 <none> 镜像
临时文件 /tmp/* 定期清理;代码中确保 finally 里删临时文件
数据库 binlog /var/lib/mysql/mysql-bin.* 设置 expire_logs_days=7;PURGE BINARY LOGS
GC 日志/dump /data/logs/gc.log, *.hprof 定期归档清理;hprof 分析完就删
Nginx 日志 /var/log/nginx/ 配置 logrotate,压缩旧日志

自动清理脚本

#!/bin/bash
# disk_cleanup.sh - 加入 crontab 每天执行
# crontab -e → 0 3 * * * /opt/scripts/disk_cleanup.sh

# 1. 清理 7 天前的应用日志
find /var/log/app -name "*.log" -mtime +7 -delete

# 2. 清理 3 天前的 gc 日志
find /data/logs -name "gc.log.*" -mtime +3 -delete

# 3. 清理 1 天前的 heap dump(已分析完的)
find /data/logs -name "*.hprof" -mtime +1 -delete

# 4. 清理 Docker 无用资源
docker system prune -f --filter "until=72h"

# 5. 清理 /tmp 下超过 3 天的文件
find /tmp -type f -mtime +3 -delete

# 6. 压缩 Nginx 旧日志
find /var/log/nginx -name "*.log.1" -exec gzip {} \;

# 7. 磁盘使用率告警
USAGE=$(df -h / | awk 'NR==2{print $5}' | tr -d '%')
if [ $USAGE -gt 85 ]; then
  echo "磁盘使用率 ${USAGE}%,超过阈值!" | mail -s "磁盘告警" ops@example.com
fi

注意:删除日志文件后如果磁盘空间没释放,说明有进程还在写入。用 lsof | grep deleted 找到这些进程,重启它们才能释放空间。或者用 > file.log 清空文件内容而不是删除文件。

5. 网络问题排查

连接超时排查路径

连接超时
Connection timed out
ping 目标IP
网络通不通?
telnet IP 端口
端口通不通?
traceroute IP
哪一跳断了?
看防火墙/安全组
iptables / 云安全组
看服务是否监听
netstat/ss 确认
# 1. 测试网络连通性
ping 10.0.1.100

# 2. 测试端口是否开放
telnet 10.0.1.100 8080
# 或用 nc(更方便)
nc -zv 10.0.1.100 8080

# 3. 追踪路由,看在哪一跳断了
traceroute 10.0.1.100

# 4. 看本机防火墙规则
iptables -L -n

# 5. 确认服务是否在监听
ss -tlnp | grep 8080

DNS 问题排查

# 1. 查询域名解析
nslookup api.example.com

# 2. 更详细的 DNS 查询
dig api.example.com

# 3. 查看本机 DNS 配置
cat /etc/resolv.conf

# 4. 清除 DNS 缓存
systemd-resolve --flush-caches # Ubuntu
dscacheutil -flushcache # macOS

TCP 连接数过多 / TIME_WAIT

# 1. 查看各状态连接数统计
ss -s

# 2. 查看 TIME_WAIT 连接数
ss -ant | awk 'NR>1{++S[$1]} END {for(a in S) print a, S[a]}'

# 3. 如果 TIME_WAIT 过多(几万+),调内核参数
# 编辑 /etc/sysctl.conf,添加:
net.ipv4.tcp_tw_reuse = 1 # 允许重用 TIME_WAIT 连接
net.ipv4.tcp_fin_timeout = 30 # 缩短 FIN_WAIT 超时
net.ipv4.tcp_max_tw_buckets = 5000 # TIME_WAIT 上限
net.core.somaxconn = 65535 # 全连接队列大小

# 4. 使配置生效
sysctl -p

网络排查路径总结

连接失败
ping通?
→ 网络OK
telnet通?
→ 端口OK
DNS解析对?
→ 域名OK
防火墙?
→ 安全组

6. 数据库问题排查

慢查询排查

接口RT高
慢查询日志
slow_query_log
EXPLAIN 分析
加索引/改SQL
# 1. 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; # 超过1秒记录

# 2. 查看最近慢查询
SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;

# 3. EXPLAIN 分析执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'PAID';

# EXPLAIN 关注字段:
# type=ALL → 全表扫描!需要加索引
# type=index → 全索引扫描
# type=ref → 索引查找,较好
# rows很大 → 扫描行数多
# Extra=Using filesort → 额外排序,考虑优化
# Extra=Using temporary → 使用临时表,考虑优化

死锁排查

# 1. 查看最近一次死锁信息
SHOW ENGINE INNODB STATUS;

# 在输出中找 "LATEST DETECTED DEADLOCK" 部分
# 关键信息:
# Transaction 1: 持有锁A,等待锁B
# Transaction 2: 持有锁B,等待锁A
# → 构成了死锁环

# 2. 查看当前锁等待
SELECT * FROM information_schema.INNODB_LOCK_WAITS;
SELECT * FROM information_schema.INNODB_LOCKS;

# 解决方案:
# 1. 统一加锁顺序(所有事务都先锁A再锁B)
# 2. 缩小事务范围,尽快提交
# 3. 添加重试机制(死锁后自动重试)
# 4. 降低隔离级别(RC 比 RR 死锁概率低)

连接池满排查

# 1. 查看当前所有连接
SHOW PROCESSLIST;
# 或看完整的(不打断)
SHOW FULL PROCESSLIST;

# 2. 查看最大连接数配置
SHOW VARIABLES LIKE 'max_connections';

# 3. 查看当前连接数
SHOW STATUS LIKE 'Threads_connected';

# 排查思路:
# 1. 连接泄漏?→ 检查代码是否有 close()
# 2. 慢查询占连接?→ SHOW PROCESSLIST 看哪些查询卡着
# 3. 连接池太小?→ 调大应用侧 maxPoolSize

# 临时处理:杀掉空闲连接
KILL <thread_id>;

主从延迟排查

# 在从库执行
SHOW SLAVE STATUS\G

# 关注字段:
# Seconds_Behind_Master → 延迟秒数(0 表示同步)
# Slave_IO_Running → Yes 才正常
# Slave_SQL_Running → Yes 才正常
# Last_Error → 最近错误

# 常见延迟原因:
# 1. 主库大事务 → 从库需要同样时间回放
# 2. 从库配置低 → CPU/IO 跟不上
# 3. 网络延迟 → 主从之间带宽不够
# 4. 从库在跑报表 → 资源争抢

# 应急处理:
# 1. 读流量切回主库(临时)
# 2. 停掉从库上的报表/分析查询
# 3. 并行复制(slave_parallel_workers)

7. Redis 问题排查

慢查询排查

# 1. 查看慢日志
SLOWLOG GET 10

# 2. 查看慢日志配置
CONFIG GET slowlog-log-slower-than

# 常见慢的原因:
# 1. BigKey → 单个 value 几 MB 甚至几十 MB
# 排查:redis-cli --bigkeys
# 解决:拆分大 key、压缩 value、改用 hash 分段存储

# 2. 复杂命令 → SORT、SINTER、LRANGE 0 -1
# 解决:避免全量操作,用 SCAN 替代 KEYS

# 3. 内存不足触发频繁淘汰
# 排查:INFO memory 看 used_memory 和 maxmemory

内存满排查

# 1. 查看内存使用情况
INFO memory

# 关注字段:
# used_memory → 已使用内存
# maxmemory → 最大内存限制
# used_memory_percent → 使用率
# mem_fragmentation_ratio → 碎片率(>1.5 需清理)

# 2. 查看淘汰策略
CONFIG GET maxmemory-policy

# 建议配置:
# 缓存场景 → allkeys-lru(淘汰最久未使用的)
# 有过期时间 → volatile-lru(淘汰有过期时间且最久未使用的)

# 3. 扫描过期 key
redis-cli --scan --pattern "session:*" | wc -l

# 4. 查看某个 key 的内存占用
MEMORY USAGE user:session:10086

缓存雪崩 / 穿透 / 击穿

问题现象排查思路解决方案
缓存雪崩 大量 key 同时过期,请求全打到 DB 看 DB 连接数是否突然飙升;检查 key 过期时间是否集中 过期时间加随机偏移(TTL + random);多级缓存;互斥锁重建
缓存穿透 查询不存在的数据,缓存没命中,DB 也没命中 看请求的 key 是否合理;是否有恶意请求 布隆过滤器拦截;缓存空值(TTL 短);参数校验
缓存击穿 热点 key 过期瞬间,大量请求打到 DB 看是否某个热点 key 刚好过期;监控该 key 的 QPS 热点 key 永不过期;互斥锁(只放一个请求回源);提前异步刷新

经验法则:Redis 内存使用率超过 80% 就要开始关注。配置好 maxmemory 和淘汰策略,避免 Redis 被 OOM killer 杀掉。线上永远不要用 KEYS 命令,用 SCAN 代替。

8. MQ 消息积压排查

排查流程

消息积压告警
Lag 数值飙升
看消费速率
还在消费吗?
消费者正常吗?
看日志/进程/线程
消息堆积量
多少条?持续多久?
# Kafka 查看消费 Lag
kafka-consumer-groups --bootstrap-server localhost:9092 \
  --describe --group my-consumer-group

# 输出关注:
# LAG 列 → 积压的消息数
# CURRENT-OFFSET → 当前消费到哪了
# LOG-END-OFFSET → 最新消息到哪了

# RabbitMQ 查看队列积压
rabbitmqctl list_queues name messages consumers

# RocketMQ 查看消费进度
./mqadmin consumerProgress -g my-consumer-group

紧急处理方案

方案操作适用场景
增加消费者 紧急扩容消费者实例数 临时流量高峰,消费能力不足
临时扩容 增加 partition + 消费者线程 Kafka 场景,partition 数是消费并行度上限
跳过非核心消息 将非核心消息转发到"待处理"队列延后消费 核心链路消息优先,非核心可以先跳过
降级消费逻辑 临时简化消费逻辑(如跳过发短信等非必要步骤) 消费逻辑太重导致处理慢

防止再发生

# 1. 死信队列配置(以 RabbitMQ 为例)
# 消费失败 N 次后自动进入死信队列
# 人工处理死信,不影响正常消费

# 2. 监控告警配置
# Lag > 1000 → P3 告警(关注)
# Lag > 10000 → P2 告警(处理)
# Lag > 100000 → P1 告警(紧急)
# 消费者下线 → P1 告警(立即处理)

# 3. 消费幂等性
# 消费逻辑必须幂等,防止重复消费导致数据错乱
# 方案:唯一ID + Redis 去重 / 数据库唯一索引

# 4. 消费限流
# 防止积压恢复时瞬间打爆下游
# 配置:prefetchCount = 10~50(不要太大)

注意:消息积压恢复时,消费速率可能飙升,瞬间打爆下游数据库或接口!务必加上消费限流,宁可多积压一会儿,也不要引发更大的故障。

9. 排查命令速查表

一张表涵盖所有常见问题的排查命令、关注指标和常见原因。

问题类型 排查命令 关注指标 常见原因
CPU飙高 top → top -Hp → printf '%x' → jstack CPU使用率、线程堆栈 死循环、正则回溯、频繁FullGC、加密操作
内存溢出 jmap -histo → jmap -dump → MAT分析 堆内存使用、对象数量 ThreadLocal泄漏、集合无限增长、大对象查询
GC频繁 jstat -gcutil → GC日志 FGC次数、OGC使用率、GC停顿时间 堆太小、内存泄漏、大对象频繁分配
磁盘满 df -h → du -sh * → find -size → lsof 分区使用率、大文件、已删除未释放 日志文件、Docker镜像、binlog、临时文件
网络超时 ping → telnet → traceroute → iptables 连通性、端口、路由、防火墙 防火墙规则、DNS问题、路由不通、服务未监听
DNS问题 nslookup → dig → /etc/resolv.conf 解析结果、DNS服务器配置 DNS配置错误、DNS缓存、域名过期
TCP连接过多 ss -ant → ss -s → netstat TIME_WAIT数量、连接状态分布 短连接太多、未设置SO_REUSEADDR、内核参数未调优
慢查询 slow_query_log → EXPLAIN → SHOW PROFILE 查询耗时、扫描行数、索引使用 缺索引、全表扫描、SQL写法差、数据量暴增
数据库死锁 SHOW ENGINE INNODB STATUS → INNODB_LOCK_WAITS 锁链、等待关系、涉及SQL 加锁顺序不一致、事务范围太大、并发更新同一行
连接池满 SHOW PROCESSLIST → SHOW STATUS 当前连接数、最大连接数、Sleep连接数 连接泄漏、慢查询占连接、连接池配置太小
主从延迟 SHOW SLAVE STATUS Seconds_Behind_Master、IO/SQL线程状态 大事务、从库配置低、网络延迟、从库跑报表
Redis慢 SLOWLOG GET → redis-cli --bigkeys 慢日志、BigKey、内存使用 BigKey、复杂命令、KEYS *、内存满频繁淘汰
Redis内存满 INFO memory → CONFIG GET maxmemory-policy used_memory、碎片率、淘汰策略 没设过期时间、BigKey、淘汰策略不对
MQ消息积压 kafka-consumer-groups --describe / rabbitmqctl list_queues Lag数量、消费速率、消费者数量 消费者挂了、消费逻辑慢、生产速度暴增
接口超时 grep ERROR 日志 → traceId链路追踪 RT分布、错误率、下游响应时间 下游慢、数据库慢查询、线程池满、GC停顿

这份速查表建议保存到手机或打印出来贴工位上。线上出问题时,先查这张表找到对应的排查命令,比百度搜索快 10 倍。