跳到主要内容

生产故障诊断

生产故障诊断通过跨层证据定位慢请求。本文负责 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 启用。

13. 参考资料​