一、CPU 相关的问题
问题1:CPU 使用率持续 100%
现象:
-
top或监控显示 CPU 使用率长期在 90% 以上 -
响应时间变慢,甚至请求超时
可能原因:
| 原因 | 具体表现 | 排查方法 |
|---|---|---|
| 死循环 | 代码中存在 while(true) 或递归没有终止条件 | jstack 查看线程栈,找到 RUNNABLE 状态的线程 |
| 频繁 GC | GC 线程占用大量 CPU | jstat -gcutil 查看 GC 频率,如果 YGC 每秒几十次就是问题 |
| 正则表达式回溯 | 复杂正则匹配长字符串时,CPU 飙升 | CPU profiling,定位到正则表达式 |
| 序列化/反序列化 | JSON 解析大对象,或频繁调用 toString() | Arthas trace 定位耗时方法 |
| 日志打印过多 | 循环内打印日志,或打印大对象 | 检查日志级别和日志量 |
案例:
java
// 问题代码:正则灾难性回溯
Pattern.compile("(a+)+b").matcher("aaaaaaaaaaaaaaaaaaaaaaac").matches();
// 这个匹配会尝试数亿次回溯,CPU 瞬间 100%
问题2:CPU 使用率忽高忽低,周期性尖峰
现象:
-
CPU 监控呈现规律的锯齿状
-
每隔几分钟出现一次峰值
可能原因:
| 原因 | 说明 |
|---|---|
| 定时任务(Cron) | 大量定时任务同时执行,如整点报表生成 |
| Full GC | 老年代满了触发 Full GC,STW 期间 CPU 被 GC 线程占满 |
| 缓存重建 | 缓存过期时间集中,大量请求同时穿透到数据库 |
| 日志滚动 | 日志切割时,压缩归档消耗 CPU |
二、内存相关的问题
问题3:内存持续增长,最终 OOM
现象:
-
内存使用率逐渐上升,重启后恢复正常
-
最终抛出
OutOfMemoryError
可能原因:
| 原因 | 具体表现 | 排查方法 |
|---|---|---|
| 内存泄漏(集合类) | HashMap、ArrayList 作为静态变量,只增不减 | 堆 dump 分析,找到最大的对象 |
| ThreadLocal 未清理 | 线程池中的 ThreadLocal 没有 remove() | 检查 ThreadLocal 的使用 |
| 缓存无过期策略 | 使用 Map 做缓存但没有淘汰机制 | 改用 Guava Cache 或 Caffeine |
| 连接池/线程池未释放 | 创建了连接但没有关闭 | 检查资源释放代码 |
| 类加载器泄漏 | 热部署、动态加载类导致元空间满 | jstat -gc 看 Metaspace 增长 |
案例:
java
// 问题代码:ThreadLocal 内存泄漏
static ThreadLocal<byte[]> threadLocal = new ThreadLocal<>();
public void process() {
threadLocal.set(new byte[10 * 1024 * 1024]); // 10MB
// 业务逻辑
// 忘记调用 threadLocal.remove()
}
// 线程池中的线程不会销毁,ThreadLocal 的数据一直存在
问题4:频繁 Full GC,但内存看起来没满
现象:
-
jstat显示 FGC 频繁(每分钟几次) -
老年代使用率不高(50% 左右)
可能原因:
| 原因 | 说明 |
|---|---|
| System.gc() 显式调用 | 某些框架或代码主动调用 System.gc(),触发 Full GC |
| 大对象直接进入老年代 | 创建了超过 -XX:PretenureSizeThreshold 的大对象 |
| 元空间(Metaspace)扩容 | 动态加载类导致 Metaspace 扩容,触发 Full GC |
| CMS GC 并发模式失败 | 老年代增长太快,CMS 来不及回收,降级为 Serial GC |
三、数据库相关的问题
问题5:响应突然变慢,数据库 CPU 飙升
现象:
-
某个接口响应时间从 50ms 突然变成 2s
-
数据库 CPU 使用率从 20% 飙升到 80%
可能原因:
| 原因 | 说明 | 排查方法 |
|---|---|---|
| 索引失效 | 数据类型不匹配、函数操作、隐式转换 | 用 EXPLAIN 看执行计划 |
| 数据倾斜 | 某些分区或分片数据量特别大 | 检查数据分布 |
| 统计信息过期 | 执行计划选错索引 | ANALYZE TABLE 更新统计信息 |
| 锁等待 | 大量请求在等待锁 | SHOW ENGINE INNODB STATUS |
| 慢查询累积 | 慢查询堆积,连接池被占满 | 开启慢查询日志,分析 TOP N |
案例:
sql
-- 问题 SQL:索引失效(隐式类型转换) -- user_id 是 varchar 类型,但传入的是数字 SELECT * FROM orders WHERE user_id = 123456; -- 正确的写法 SELECT * FROM orders WHERE user_id = '123456';
问题6:数据库连接池满,应用报错
现象:
-
日志中出现
Timeout waiting for connection from pool -
部分请求失败
可能原因:
| 原因 | 说明 |
|---|---|
| 慢 SQL 占满连接 | 慢 SQL 执行时间长,一直占用连接 |
| 事务未提交 | @Transactional 方法异常后没有回滚,事务未释放 |
| 连接泄露 | 代码中获取了连接但没有关闭 |
| 连接池配置过小 | 业务增长后,原有的连接池大小不足 |
| 下游服务慢 | 调用下游接口慢,导致线程阻塞,连接无法释放 |
四、网络相关的问题
问题7:接口响应时间不稳定,偶尔超时
现象:
-
P99 响应时间远高于平均值
-
偶尔出现超时告警
可能原因:
| 原因 | 说明 | 排查方法 |
|---|---|---|
| GC 停顿 | Full GC 导致 STW,请求被挂起 | 查看 GC 日志,对比超时时间点 |
| 慢 SQL | 某些 SQL 在特定条件下执行慢 | 开启慢查询日志,对比慢的 SQL |
| 网络抖动 | 跨机房调用,网络偶发丢包 | ping 和 mtr 检查网络质量 |
| 服务重启 | 发布或健康检查失败导致重启 | 查看容器/服务重启记录 |
| 限流触发 | 触发限流后排队等待 | 检查限流器配置和监控 |
问题8:请求堆积,响应时间线性增长
现象:
-
流量上升时,响应时间同步上升
-
流量下降后,响应时间恢复慢
可能原因:
| 原因 | 说明 |
|---|---|
| 线程池队列满 | 使用固定大小线程池,队列满了开始排队 |
| 背压(Backpressure) | 下游处理慢,上游被反压 |
| 连接池耗尽 | 数据库或 HTTP 连接池耗尽,请求等待 |
| 异步处理瓶颈 | 消息队列消费能力不足,消息堆积 |
五、存储/IO 相关的问题
问题9:磁盘 IO 高,服务响应慢
现象:
-
iostat显示%util接近 100% -
读写延迟高
可能原因:
| 原因 | 说明 | 排查方法 |
|---|---|---|
| 日志刷盘频繁 | 同步日志配置,每次写入都刷盘 | 检查日志配置,使用异步日志 |
| 大文件读写 | 文件操作频繁,或文件过大 | lsof 看哪些进程在读写文件 |
| 数据库刷脏页 | InnoDB 刷脏页时 IO 压力大 | 调整 innodb_io_capacity |
| swap 换入换出 | 内存不足,使用 swap 导致 IO | free -h 查看 swap 使用 |
| 快照/备份 | 定时备份任务与业务争抢 IO | 错峰执行备份 |
问题10:磁盘满,服务只读或崩溃
现象:
-
日志报
No space left on device -
数据库进入只读模式
可能原因:
| 原因 | 说明 |
|---|---|
| 日志未轮转 | 应用日志或 GC 日志未配置轮转 |
| 慢查询日志增长 | 慢查询日志长期未清理 |
| dump 文件未清理 | 堆 dump 文件占用大量空间 |
| binlog 堆积 | MySQL binlog 未清理,或清理策略不合理 |
| 临时文件 | 大排序、大连接产生的临时文件 |
六、并发/锁相关的问题
问题11:死锁,事务回滚
现象:
-
日志出现
Deadlock found when trying to get lock -
部分请求失败
可能原因:
| 原因 | 说明 |
|---|---|
| 多表更新顺序不一致 | 事务 A 更新表 1→表 2,事务 B 更新表 2→表 1 |
| 主键范围锁间隙 | RR 隔离级别下,范围查询会锁间隙 |
| 唯一键冲突 | 并发插入相同唯一键,产生锁等待和死锁 |
| 分布式锁死锁 | 多个锁的获取顺序不一致 |
案例:
sql
-- 事务 A UPDATE accounts SET balance = balance - 100 WHERE id = 1; UPDATE accounts SET balance = balance + 100 WHERE id = 2; -- 事务 B(同时执行,顺序相反) UPDATE accounts SET balance = balance - 50 WHERE id = 2; UPDATE accounts SET balance = balance + 50 WHERE id = 1; -- 互相等待对方释放锁,死锁
问题12:高并发下出现大量锁等待
现象:
-
数据库线程数飙升
-
大量
LOCK WAIT状态
可能原因:
| 原因 | 说明 |
|---|---|
| 热点行更新 | 多线程更新同一行数据,如库存扣减 |
| 事务范围过大 | 事务中包含 RPC 调用或复杂计算,长时间持有锁 |
| 未提交事务 | 代码中开启了事务,异常时未回滚或提交 |
| 悲观锁滥用 | SELECT ... FOR UPDATE 使用不当 |
七、排查工具速查表
| 问题类型 | 快速排查命令 |
|---|---|
| CPU 高 | top -H -p <pid> 看线程 CPU,jstack 找线程栈 |
| 内存高/OOM | jmap -dump:live,format=b,file=heap.hprof <pid> 生成堆 dump |
| GC 频繁 | jstat -gcutil <pid> 1000 实时看 GC |
| 慢 SQL | EXPLAIN 看执行计划,mysqldumpslow 分析慢查询日志 |
| 锁等待 | SHOW ENGINE INNODB STATUS,SELECT * FROM sys.innodb_lock_waits |
| 连接池满 | jstack 看等待连接的线程,检查连接池配置 |
| IO 高 | iostat -x 1,iotop |
| 网络问题 | netstat -anp,ss -tunap,ping,mtr |
1064

被折叠的 条评论
为什么被折叠?



