生产故障诊断
生产故障诊断通过跨层证据定位慢请求。本文负责 JVM、线程、SQL 与网络的联合分析。
1. 面试核心
先记住这几条,再读正文- 耗时分解。 端到端耗时包含排队、应用执行和下游等待。
- 低 CPU 下的慢请求。 CPU 较低时,连接池、锁和网络等待仍可抬高 P99。卡 · 综合题
- 线程状态。 RUNNABLE 线程也能等待 native I/O,状态名不足以证明 CPU 忙。
- 虚拟线程 pinning。 JDK 21 虚拟线程在 synchronized 内阻塞会 pin 住载体线程。卡 · JDK 诊断命令与版本
- 根因证据。 重启同时重置多种状态,恢复结果不足以确定根因。
2. 时间、范围与变更
先确定异常时间、路由、实例和租户范围。记录错误码、吞吐、P50/P99 与最老请求年龄。
将发布、配置、流量和依赖事件放在同一窗口。只有新版本异常时,比较版本差异。多个服务同时异常时,检查共同依赖。
止血同时保留证据。摘除异常实例前,采集可控的线程栈与 JFR。数据库现场也需保存,避免重启后丢失等待关系。
3. 端到端耗时
比较客户端、网关和服务端耗时。Trace 只显示已埋点的阶段,缺失阶段需补指标。
例如服务处理只用 20 ms,网关总耗时却是 2 s。额外时间可来自建连、连接池等待或重试。应逐段对照,避免把差值全部归因于网络。
CPU 较低时,也要核对单核热点与容器 CPU 配额。实例平均值会掩盖局部计算瓶颈。
4. JVM 证据
GC 暂停需与慢请求时间对齐。回收后堆基线持续上升,才值得继续检查对象保留。
进程 RSS 还包含 native memory、线程栈等内容。容器 OOMKilled 可发生在 Java 堆尚有空间时。
JFR 可关联分配、锁、Socket、文件 I/O 和执行采样。采集堆转储前评估停顿与磁盘空间。类直方图也有开销,应按命令影响等级选择。
5. 线程栈
连续采集线程栈,比较相同线程的停留位置。
- 大量 BLOCKED 线程指向 monitor 竞争。
- worker 忙且队列增长,说明任务处理跟不上到达量。
- JDBC 连接等待需结合连接池 pending 与事务时间。
- RUNNABLE 需结合 CPU 采样判断实际执行。
线程名称与池指标应能互相对应。虚拟线程还需区分 JDK 版本和底层资源等待。
6. 数据库
应用侧记录连接池等待、事务时长与 SQL 次数。数据库侧检查锁等待、执行计划和实际扫描行数。
慢 SQL 可来自统计信息变化、长事务或调用次数增长。索引存在时,范围过大仍会读取大量数据。
MySQL 8.0 的 EXPLAIN ANALYZE 会实际执行查询。生产重查询需先评估影响。锁诊断细节见 InnoDB 锁。
7. 网络
分开观察 DNS、TCP 建连、TLS 与首字节耗时。连接 reset、重传和代理日志用于补充证据。
长连接会继续访问原地址。负载均衡与 NAT 的空闲超时也需核对。TCP 状态含义见 TCP 连接。
HTTP/2 的多个 stream 共享 TCP 连接。连接丢包引发重传时,会影响该连接的交付进度。客户端排队也能使服务端看不到慢请求。
8. 跨层证据
例如 JDBC pending、数据库锁等待同时增长。线程栈也持续停在同一查询时,等待链更明确。
时间相关性用于提出假设。再通过阻塞关系或受控干预验证原因。每次改变一个主要变量,并保留对照。
修复后在相近负载下比较原始 SLI。等待指标和业务结果也需恢复,才能确认效果。
9. 取证准备
预先配置诊断权限、GC 日志与证据保存路径。仪表盘关联服务版本、线程池、SQL 和网络阶段。
Runbook 记录采集开销和升级联系人。故障结束后,把有效证据转成指标或回归检查。
10. JDK 诊断命令与版本
以下命令适用于具备诊断权限的 JDK 21 进程。将 1234 换为目标 PID,文件名使用可写路径。
jcmd 1234 Thread.print -l
jcmd 1234 JFR.start name=incident settings=profile duration=60s filename=incident.jfr
jcmd 1234 VM.native_memory summary
Thread.print 用于平台线程栈与锁信息。虚拟线程可另用 jcmd 1234 Thread.dump_to_file -format=json threads.json。JFR 的 profile 配置比 default 有更高采集开销。
NMT 命令需在启动时启用 -XX:NativeMemoryTracking=summary。NMT 统计 JVM 内部 native 分配,并非进程 RSS 的完整清单。
JDK 21 中,虚拟线程在 synchronized 内阻塞会 pin 住载体。native 调用也会造成 pinning。可用 JFR 的 jdk.VirtualThreadPinned 事件定位。长时间阻塞的同步代码可评估改用 ReentrantLock。
11. 常见问题
11.1 低 CPU 与高 P99
先核对实例、单核和容器配额,再看慢请求等待。线程池队列、JDBC pending 与 Trace 可缩小范围。CPU 平均值较低仍需这些运行证据。
11.2 重启恢复的解释范围
重启会重置线程、连接、缓存和本地状态。恢复只能说明这些变化中的某项改善了症状。根因判断仍需重启前证据与复现结果。
12 · 面试题卡片
12.1 接口 P99 升高但 CPU 正常,如何联合排查
出现公司:欢聚时代
题型:综合题
考察重点
- 理解影响范围、发布时线和分段耗时。
- 理解JVM、线程池、连接池、SQL 与网络证据。
- 理解止血、取证、假设验证和修复回归。
参考回答
先确认异常时间、路由、实例与版本,并同步保存现场。
用网关与 Trace 分解排队、应用和下游耗时。CPU 正常时,继续看单核热点、配额和等待指标。
JVM 侧关联 GC、JFR 与连续线程栈。数据库侧核对锁等待和实际扫描量。网络侧核对建连、首字节与重传。形成假设后只改变一个变量验证。修复结果用同负载下的 SLI 与等待指标确认。
12.2 怎样采集 JVM 等待证据,JDK 21 的虚拟线程 pinning 如何定位?
出现公司:暂无已核验的公开来源(本站自测)
考察重点:线程栈、JFR、NMT 与 JDK 21 的 pinning。
相关内容:JDK 诊断命令与版本。
参考回答
用线程栈和 JFR 关联 JVM 等待,JDK 21 的虚拟线程 pinning 重点看 jdk.VirtualThreadPinned 事件及其调用栈。
平台线程可用 jcmd 1234 Thread.print -l 采集栈,短时 JFR 用于关联锁、I/O 和执行采样。JDK 21 中要检查 synchronized 内阻塞造成的 pinning,native 调用和外部资源等待也需独立核对。若还需分析本地内存,NMT 要提前用 -XX:NativeMemoryTracking=summary 启用。