常见性能问题及原因分析

一、CPU 相关的问题

问题1:CPU 使用率持续 100%

现象

  • top 或监控显示 CPU 使用率长期在 90% 以上

  • 响应时间变慢,甚至请求超时

可能原因

原因具体表现排查方法
死循环代码中存在 while(true) 或递归没有终止条件jstack 查看线程栈,找到 RUNNABLE 状态的线程
频繁 GCGC 线程占用大量 CPUjstat -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

可能原因

原因具体表现排查方法
内存泄漏(集合类)HashMapArrayList 作为静态变量,只增不减堆 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 导致 IOfree -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 找线程栈
内存高/OOMjmap -dump:live,format=b,file=heap.hprof <pid> 生成堆 dump
GC 频繁jstat -gcutil <pid> 1000 实时看 GC
慢 SQLEXPLAIN 看执行计划,mysqldumpslow 分析慢查询日志
锁等待SHOW ENGINE INNODB STATUSSELECT * FROM sys.innodb_lock_waits
连接池满jstack 看等待连接的线程,检查连接池配置
IO 高iostat -x 1iotop
网络问题netstat -anpss -tunappingmtr

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值