线上CPU飙高怎么排查?从日志→代码→根因的三层定位法,附3个真实案例

发布时间:2026/7/18 23:13:47

线上CPU飙高怎么排查?从日志→代码→根因的三层定位法,附3个真实案例
生产问题排查CPU 飙高、内存泄漏、慢 SQL 三场景实战问题场景线上服务突然变慢CPU 飙到 90%日志刷屏但看不出根因——给你 15 分钟你会怎么排查读完收获① 掌握 CPU/内存/慢SQL 三类问题的标准排查路径 ② 获得可直接套用的 jstack/jmap/Arthas 分析模板 ③ 学会从现象→日志→代码→根因的逆向定位思维本文是《Java 后端核心知识图谱》系列第 1 篇共 172 篇。一、接口响应慢分层排查体系线上收到接口变慢的告警最大的忌讳是一上来就猜测原因。标准做法是分层排查逐层排除。第一层链路追踪定位通过 SkyWalking / Zipkin / Jaeger 查看调用链定位耗时最长的 Span。判断慢在哪个环节——应用代码、SQL 查询、Redis 操作、对外 HTTP 调用、MQ 发送。第二层应用层Arthas 三板斧dashboard# 查看 JVM 整体状态GC 频率、堆使用率、线程状态thread-n5# 定位 CPU 使用率最高的 5 个线程trace ClassName method# 追踪方法调用链输出每一步耗时watchClassName method{params, returnObj, throwExp}-x3# 观察入参/返回值monitor-c5ClassName method# 统计方法调用次数、成功/失败率、平均/最大耗时vmtool--actiongetInstances--classNamecom.xxx.Entity--limit10# 查看大对象数量trace是最常用的命令——它以树状结构输出方法调用链的每一步耗时一目了然地揭示哪个子调用耗时最长---[95.00%] DispatcherServlet.doDispatch() ---[92.00%] OrderController.createOrder() # 92% 时间在 Controller ---[88.00%] OrderService.createOrder() # 88% 在 Service ---[70.00%] deliveryService.query() # 70%瓶颈在此 ---[68.00%] jdbc.executeQuery() # SQL 耗时 68%由此可直接定位到 SQL 慢的问题。第三层数据库层常见慢原因未走索引EXPLAIN 显示typeALL全表扫描索引区分度差走了索引但rows扫描行数仍然很大回表过多需要通过覆盖索引消除回表锁等待通过SHOW ENGINE INNODB STATUS查看锁信息连接池耗尽通过 Druid / HikariCP 监控面板查看活跃连接数第四层缓存层检查 Redis 慢日志SLOWLOG GET 10排查大 key单个 Hash 包含数十万字段导致单次操作缓慢及热 key某个 key 的 QPS 极高导致单分片瓶颈问题。第五层外部依赖使用CompletableFuture将串行调用改为并行。配置超时与熔断Sentinel / Hystrix防止单个下游服务缓慢拖垮整个调用链路。二、工具速查表生产环境中机器负载高是常见问题掌握以下命令是高效排查的前提。JVM 层面场景工具关键命令CPU 飙高top jstack / Arthastop -H -p PID,thread -n 5内存泄漏jmap MAT / Arthasheapdump /tmp/dump.hprof, 分析 Dominator TreeGC 频繁jstat / GC 日志jstat -gcutil PID 1000,-Xlog:gc*死锁jstackjstack PID | grep BLOCKED -A 15类加载问题Arthasclassloader -t查看类加载器树方法耗时Arthas tracetrace com.xxx.Service method -n 5SQL 慢查Druid 监控 / SlowLogDruid 内置 SQL 监控面板Redis 慢Redis slowlogSLOWLOG GET 10OS 层面场景命令关注点机器整体负载top/htopload average1/5/15 分钟CPU 使用率us/sy/wa内存使用CPU 详情mpstat -P ALL 1各核心使用率分布判断是否为单核打满内存详情free -havailable vs freebuffer/cache 可回收磁盘 IOiostat -x 1%util 接近 100% 表示磁盘瓶颈await 为 IO 等待时间磁盘空间df -h各分区使用率日志爆满是常见非 OOM 的内存杀手网络连接netstat -antp | grep ESTABLISHED | wc -l连接数是否正常大量 TIME_WAIT 表示短连接过多端口监听ss -tlnp比 netstat 更快确认服务端口是否正常监听文件句柄lsof -p PID | wc -l句柄泄漏导致 “Too many open files”ulimit -n检查上限进程线程数cat /proc/PID/status | grep Threads线程数异常增长表示可能线程泄漏系统日志dmesg -T | tail、journalctl -u app-nameOOM Killer 日志、coredump、系统级错误排查经历的标准描述逻辑先用top获取整体状况——CPU 高内存高load average 异常若为 Java 进程则切入 Arthasdashboardthread -n 5若磁盘 IO 高则通过iostat确认——可能是大量日志写入或数据库 IO若为网络问题则通过netstat查看连接数——排查下游慢导致的连接堆积定位后JVM 层面使用 ArthasSQL 层面使用 EXPLAINOS 层面使用上述命令三、内存泄漏排查全流程线上内存持续增长最终导致 OOM应如何排查完整流程分为七步Step 1确认是泄漏还是正常增长使用jstat -gcutil PID 1000持续观察老年代使用率——若每次 Full GC 后老年代使用率持续上升如从 60% 到 70% 再到 80%则为泄漏若 GC 后能回落到正常水位仅为对象创建速度过快则属于正常情况需调优参数。Step 2在 OOM 发生前 dump 堆快照jmap-dump:formatb,file/tmp/heap.hprof PID堆内存较大时 jmap 可能触发 STW可加-F参数强制 dump。生产环境建议配置-XX:HeapDumpOnOutOfMemoryError实现自动 dump。Step 3使用 MAT 分析 Dominator Tree用 MAT 打开 hprof 文件点击 Dominator Tree支配树按 Retained Heap 排序。Dominator Tree 的核心概念A 支配 B 表示从 GC Roots 到 B 的所有路径都经过 A因此 A 的 Retained Heap 等于 A 被回收后能一并释放的内存总量。该视图可快速定位最大的垃圾源头。Step 4定位具体对象在 Dominator Tree 中找到 Retained Heap 最大的对象右键选择 “Path to GC Roots”再选择 “exclude weak/soft references”即可看到完整引用链。例如HashMap$Node → ArrayList → SomeService.staticList表明是静态集合不断 add 导致的内存泄漏。Step 5分析代码根因常见泄漏场景静态 Map 不断 put缓存未设置过期时间或未设置容量上限ThreadLocal 未 remove线程池复用线程Value 永远不会被释放监听器/回调未注销Observer 模式中遗漏 removeListener 调用数据库连接未关闭连接池中的连接持有大量结果集未释放内部类持有外部引用非静态内部类隐式持有Outer.this引用Step 6修复验证修复代码后进行灰度发布观察老年代在 GC 后的使用率是否趋于稳定。Step 7建立预防机制配置 JVM 参数-XX:HeapDumpOnOutOfMemoryError、设置老年代使用率超过 80% 的监控告警、定期 Review 静态集合和 ThreadLocal 的使用情况。四、死锁排查症状部分接口超时无响应其他接口正常线程池活跃线程数持续升高CPU 使用率不高线程均在阻塞等待而非自旋。# 使用 jstack dump 线程快照jstack PID/tmp/jstack.log# 查看文件末尾JVM 会自动检测死锁grepdeadlockjstack.log-i-A20jstack 输出的底部会直接打印死锁信息Found one Java-level deadlock:以及具体的线程和锁的持有/等待关系。常见死锁原因嵌套锁获取顺序不一致线程 A 先锁 a 再锁 b线程 B 先锁 b 再锁 a环状等待链线程 A 持有锁 A 等待锁 B线程 B 持有锁 B 等待锁 C线程 C 持有锁 C 等待锁 A数据库死锁两个事务以不同顺序更新同一组数据行避免策略统一加锁顺序使用tryLock(timeout)替代synchronized——超时后释放已持有锁并重试减小锁粒度缩短锁持有时间优先使用无锁数据结构ConcurrentHashMap、AtomicInteger替代显式加锁五、连接池耗尽一个慢 SQL 的雪崩链场景还原某个慢 SQL 占用了大量连接执行时间超过 30s后续请求因无法获取连接而阻塞等待连接池被占满如 Druid 默认 maxActive20线程池队列随之饱和触发拒绝策略最终导致大面积超时。这就是典型的雪崩效应——一个慢 SQL 拖垮整个应用。排查步骤# Arthas dashboard 查看线程状态——大量线程 BLOCKED 在 Druid 获取连接处# Druid 监控页面查看活跃连接数和等待队列长度# 定位持有连接时间最长的线程极可能是慢 SQL 的源头解决方案级别措施紧急手动 kill 慢 SQLSHOW PROCESSLIST定位后执行KILL ID释放连接短期设置 SQL 超时DruidqueryTimeout10s超时自动断开并释放连接长期慢 SQL 优化加索引/改写 SQL/加缓存连接池隔离核心与非核心业务使用不同数据源连接池参数调优六、Full GC 频繁调优现象线上每隔几分钟触发一次 Full GC每次 STW 持续 3-5 秒。排查步骤分析 GC 日志——-Xlog:gc*:file/tmp/gc.logJDK 9使用 GCeasy 或 GCViewer 工具分析。重点观察每次 Young GC 后晋升到老年代的对象数量Promotion Size、老年代何时被填满从而触发 Full GC。常见原因及优化方向原因现象优化年轻代太小对象未经过足够轮次就晋升老年代调大-Xmn设为总堆的 1/3 到 1/2Survivor 太小Minor GC 时对象直接进入老年代调小-XX:SurvivorRatio增大 Survivor 占比大对象直接进老年代GC 日志中出现 “humongous allocation”代码层面避免大对象或调大-XX:PretenureSizeThreshold老年代太小正常晋升也频繁触发 Full GC调大-Xmx但需先排查以上三项确认非内存泄漏MetaSpace 满GC 日志中出现 “Metadata GC Threshold”调大-XX:MaxMetaspaceSize内存泄漏GC 后老年代使用率持续上升参照第三章流程排查泄漏实战案例某次压测发现每 2 分钟一次 Full GCSTW 4sGC 日志显示每次 Young GCEden 200MB后约有 80MB 对象晋升老年代。分析发现 Survivor 区仅 20MB根本容纳不下存活对象导致对象直接进入老年代。调整方案SurvivorRatio 从 8 调整为 3Eden:S0:S13:1:1Survivor 扩大至 50MB晋升量降至约 10MB/次Full GC 消失。七、性能排查六步法从现象到根因、从宏观到微观的标准化排查流程Step 1确认问题范围1 分钟先回答三个问题是所有接口都慢还是仅某个接口慢全局 vs 局部是一直慢还是某个时间点突然变慢持续 vs 突发慢在哪个环节通过 SkyWalking / Zipkin 查看调用链各 Span 的耗时全局慢大概率是 JVM GC / 连接池 / 机器资源问题。局部慢大概率是某个 SQL / 下游调用 / 代码逻辑问题。Step 2定位瓶颈层级5 分钟用户请求 → 网关(Nginx) → 应用(Spring) → 业务代码 → SQL/Redis/RPC │ │ │ │ ↓ ↓ ↓ ↓ 超时配置 线程池/GC Arthas trace EXPLAIN/SlowLog 连接数 内存 Watch 观察 Redis 慢日志Step 3应用层排查Arthas 三板斧dashboard# 一眼看全局GC 频率、堆使用率、线程数、CPUthread-n10# 定位 CPU 最高的线程排查死循环或自旋trace ServiceImpl method-n20# 追踪方法调用链精确到每一步的耗时Step 4数据库层排查若 trace 指向 SQL按以下顺序分析EXPLAIN 查看执行计划type 是否为 ALL全表扫描、key 使用了哪个索引、rows 扫描行数是否合理查看慢查询日志通过SHOW PROFILES查看具体哪个阶段慢Sending data / Creating tmp table / Sorting锁分析通过SHOW ENGINE INNODB STATUS排查锁等待和死锁连接池通过 Druid / HikariCP 监控面板查看活跃连接数、等待队列、平均执行时间Step 5缓存/中间件层排查RedisSLOWLOG GET 20查看近期慢命令关注大 keyMEMORY USAGE key和热 keyMQ查看消费者积压的 lag 值积压过多时增加消费者实例RPC检查超时配置是否合理、熔断器是否已打开Step 6验证修复效果修复后必须有对比数据来证明效果。观察窗口至少持续 1 小时以排除缓存预热、JIT 编译等干扰因素。八、慢 SQL 优化实战标准流程开启慢查询日志long_query_time1smin_examined_row_limit1000EXPLAIN 分析执行计划重点关注typeALL 最差至少应达到 range、key是否命中索引、rows预估扫描行数、ExtraUsing filesort 表示需排序优化、Using temporary 表示使用了临时表常见优化手段建立合适的索引联合索引遵循最左前缀原则删除无用索引无用索引会拖累写性能改写 SQL用 JOIN 替代子查询、用 UNION ALL 替代 OR、避免SELECT *、避免在 WHERE 中对列使用函数大偏移量分页采用先查 ID 再关联策略大表考虑分库分表或同步至 ES实战案例Oracle 迁移 OceanBase 后查询从 2s 变为 5min现象某客户交割查询Oracle 上执行 2 秒OceanBase 上执行 5 分钟后超时排查过程拆分 SQL 逐块测试每次去掉一个条件测量耗时差异发现瓶颈由三部分组成PG 函数最大头约 80% 的开销 spotTrade NOT EXISTS 子查询 swapTrade/deals NOT EXISTS 子查询根因OceanBase 优化器对 PL/SQL 函数在 WHERE 中的处理方式为逐行调用——表有 410 万行两个子查询各扫一遍PG 函数每行都调用一次。410 万行乘以 2 次全量扫描再乘以每次函数查参数表导致时间爆炸式增长最终策略在 Java 层面预计算日期参数避免在 SQL 条件侧调用函数通用经验大表超过 100 万行加上 WHERE 中对列使用函数调用几乎必然导致性能灾难。函数务必放在变量侧预计算不可放在条件侧。数据库迁移不能仅验证功能正确性必须使用真实数据量进行性能回归测试。实战案例接口从 200ms 逐渐变慢至 3s现象某查询接口的响应时间随时间推移从 200ms 逐步增长至 3s重启后恢复正常之后再次逐步恶化排查通过 jstat -gcutil 持续观察发现老年代使用率持续上升Full GC 后仅回落极少部分。执行 heap dump 后使用 MAT Dominator Tree 分析发现一个静态 Map 中堆积了 50 万条数据每次请求都向其中 put 数据但从不清理根因缓存 Map 配置了Cacheable但未设置过期时间和最大容量限制修复改用 Caffeine CachemaximumSize10000, expireAfterWrite30min问题彻底解决九、性能优化的五层优先级按 ROI 从高到低排列业务优化减少无效请求 缓存避免查数据库 索引优化查得更快 代码优化算得更快 硬件加机器 ROI 最高 效果最明显 常用手段 最后手段第一优先级业务层优化ROI 最高、风险最低减少不必要的查询将循环中逐条查数据库改为批量查询将反复查部门名称改为一次 JOIN减少数据传输量将 10 万行全量返回前端改为后端分页将SELECT *改为仅查询需要的列异步化下单后的短信/邮件/推送通知通过消息队列异步处理削峰填谷秒杀场景使用 MQ 或令牌桶限流第二优先级缓存层浏览器缓存 → CDN 缓存 → Nginx 缓存 → Redis 缓存 → Caffeine 本地缓存三板斧最大容量防 OOM 过期时间防脏数据 布隆过滤器防穿透第三优先级数据库层索引优化 → SQL 改写避免 WHERE 中对列使用函数用 UNION ALL 替代 OR→ 读写分离 → 分库分表 → ES 同步第四优先级代码层CompletableFuture 并行调用替代串行 → 连接池调优 → 序列化优化Protobuf 替代 JSON→ 数据结构选择频繁 contains 操作使用 Set 而非 List第五优先级JVM 与基础设施GC 调优 → 硬件升级SSD、增加内存→ 水平扩展增加实例 负载均衡总结分层定位、逐层排除是线上问题排查的核心思维——从链路追踪SkyWalking/Zipkin确定慢在哪个 Span到 Arthas trace 树状输出方法调用链的每一步耗时再到数据库层的 EXPLAIN 分析和缓存层的 Redis 慢日志。三个最高频的生产事故各有标准排查路径CPU 飙高通过top -Hp PID定位线程后结合jstack定位代码行或 Arthasthread -n 5一步到位内存泄漏走jmap -dump→ MAT Dominator Tree按 Retained Heap 排序→ Path to GC Roots 的完整链路其中静态集合无限增长和 ThreadLocal 未 remove 是最常见的泄漏来源慢 SQL用 EXPLAIN 关注 type/key/rows/Extra 四列联合索引遵循最左前缀原则避免在 WHERE 中对列使用函数。连接池耗尽是最典型的雪崩场景——一个慢 SQL 占用连接 → 连接池满 → 线程阻塞等待 → 线程池饱和 → 拒绝策略触发 → 大面积超时。紧急止血用KILL ID长期方案靠 SQL 超时机制加连接池隔离。性能优化的优先级按 ROI 排序为业务优化减少无效请求 缓存避免查数据库 索引优化查得更快 代码优化算得更快 硬件加机器核心能力是根据现象判断哪种手段的 ROI 最高。系列专栏《Java 后端核心知识图谱》172 篇Java专栏下一篇《Redis 核心原理六大数据结构 RDB/AOF双引擎 缓存一致性方案》配套代码PrismAI如果本文帮你省下了一次半夜被报警叫醒排查的时间欢迎收藏 点赞

相关新闻

项目健康度量化模型:构建多维度风险预警评分卡的技术实现

项目健康度量化模型:构建多维度风险预警评分卡的技术实现

2026/7/18 23:13:47

全文阅读约5分钟据美国项目管理协会(PMI)《PMBOK指南》第七版明确指出,项目绩效域的健康监测应贯穿项目全生命周期,通过可量化的度量指标实现风险的前置识别与动态管控。PMI 2025年全球峰会进一步强调,将多维度风险因子…

Cursor支付集成到底要不要自己写网关?资深支付架构师对比4种方案ROI,第3种已被3家独角兽紧急停用

Cursor支付集成到底要不要自己写网关?资深支付架构师对比4种方案ROI,第3种已被3家独角兽紧急停用

2026/7/18 23:13:47

更多请点击: https://codechina.net 第一章:Cursor支付功能集成的行业现状与核心挑战 当前,AI原生开发工具如Cursor正加速向商业化闭环演进,支付功能集成已成为其平台能力扩展的关键环节。然而,与成熟IDE(…

人工智能专业就业必考证书指南

人工智能专业就业必考证书指南

2026/7/18 23:13:47

引言:人工智能时代,证书是就业的“敲门砖”随着人工智能技术的飞速发展,相关岗位需求呈现爆发式增长。对于人工智能专业的学生和从业者而言,如何在激烈的竞争中脱颖而出,获得心仪的offer?除了扎实的专业知识…

Linux Shell编程实战:需求驱动与自动化测试

Linux Shell编程实战:需求驱动与自动化测试

2026/7/19 1:03:51

1. 需求驱动的Linux Shell编程概述在Linux系统管理和自动化任务处理中,Shell脚本是最直接有效的工具之一。不同于传统的教程式学习,需求驱动(Requirement-Driven)的Shell编程方法更注重从实际项目需求出发,通过解决具体问题来掌握核心技能。这…

Vercel 绑定自定义域名极简配置指南

Vercel 绑定自定义域名极简配置指南

2026/7/19 1:03:51

Vercel 绑定自定义域名极简配置指南 将自定义域名绑定到 Vercel 部署的项目中,核心逻辑仅需两步:在 Vercel 声明域名,在域名服务商处指向 Vercel。以下是具体配置流程及常见避坑指南。 1. 在 Vercel 面板添加域名 进入 Vercel 项目管理后台&a…

需求驱动的Shell脚本开发实践与优化

需求驱动的Shell脚本开发实践与优化

2026/7/19 1:03:51

1. 需求驱动的Shell编程理念在Linux系统管理中,Shell脚本一直扮演着不可替代的角色。但传统教学往往从语法开始,导致学习者陷入"知道命令却不会解决问题"的困境。需求驱动开发(Requirement-Driven Development)方法彻底改变了这一局面——它要…

Hibernate与NHibernate核心解析与实战对比

Hibernate与NHibernate核心解析与实战对比

2026/7/19 1:03:51

1. Hibernate与NHibernate核心概念解析Hibernate和NHibernate作为业界主流的对象关系映射(ORM)框架,本质上解决了面向对象编程语言与关系型数据库之间的"阻抗不匹配"问题。我在实际项目中使用这两个框架已有8年时间,发现很多开发者对它们的理解…

OCR C++ Tesseract用户自定义模式

OCR C++ Tesseract用户自定义模式

2026/7/19 1:03:51

这个程序演示设置一个配置文件,以提高识别的正确率。输入图片比较长,截图成了好几段。可以看到输入图片是有规律的,2个字母4个数字1个字母。人眼很容易看出来这个规律,但是识别程序它不知道有这个规律,就可能会出现识别…

Rust AI 工具发布为单二进制:静态链接把一切装进一个文件的技术要点

Rust AI 工具发布为单二进制:静态链接把一切装进一个文件的技术要点

2026/7/19 0:53:51

Rust AI 工具发布为单二进制:静态链接把一切装进一个文件的技术要点专栏: AI / AI CLI / Rust编译部署一、为什么单二进制是 AI CLI 工具的终极交付形态 我刚把上周写的 AI 代码审查工具发给朋友试用,他第一句话不是夸功能,而是问&#xff1a…

微服务进阶:服务网格与Istio

微服务进阶:服务网格与Istio

2026/7/19 0:03:49

541|微服务进阶:服务网格与Istio 上篇文章我们聊了微服务的基本概念和拆分方法。 但微服务多了,问题也多了: 服务之间怎么通信? 怎么监控每个服务的调用链路? 熔断、限流、重试怎么做? 安全认证怎么统一? 以前这些都靠SDK库(比如Hystrix、Feign),每个服务都要集成…

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

2026/7/19 0:03:49

一、零售门店全域协同业务背景与行业痛点 1.1 门店超级终端设备矩阵(连锁便利店/商超标准配置) 自助收银Kiosk一体机:顾客结算、自助核销优惠券、商品素材预览;运营折叠平板:店长后台商品上新、图片录入、活动配置、…

噗叽短视频界面分析

噗叽短视频界面分析

2026/7/19 0:03:49

1 和小红书类似,可以采用类似判断方法------------其实他比小红书好判断,因为他没有图片,控件位置几乎是固定的,都不用判断------------2 因为他没有点赞按钮------------而且几乎所有控件位置都是完全一样的,所以我就…

微服务进阶:服务网格与Istio

微服务进阶:服务网格与Istio

2026/7/19 0:03:49

541|微服务进阶:服务网格与Istio 上篇文章我们聊了微服务的基本概念和拆分方法。 但微服务多了,问题也多了: 服务之间怎么通信? 怎么监控每个服务的调用链路? 熔断、限流、重试怎么做? 安全认证怎么统一? 以前这些都靠SDK库(比如Hystrix、Feign),每个服务都要集成…

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

零售超级终端全域协同:ShareKit 碰一碰商品流转业务落地案例

2026/7/19 0:03:49

一、零售门店全域协同业务背景与行业痛点 1.1 门店超级终端设备矩阵(连锁便利店/商超标准配置) 自助收银Kiosk一体机:顾客结算、自助核销优惠券、商品素材预览;运营折叠平板:店长后台商品上新、图片录入、活动配置、…

噗叽短视频界面分析

噗叽短视频界面分析

2026/7/19 0:03:49

1 和小红书类似,可以采用类似判断方法------------其实他比小红书好判断,因为他没有图片,控件位置几乎是固定的,都不用判断------------2 因为他没有点赞按钮------------而且几乎所有控件位置都是完全一样的,所以我就…