Skip to content

Spring Boot 生产监控与线上问题排查

线上排查不是“登录服务器看日志”,而是围绕一次异常建立完整证据链:什么时候开始、影响谁、哪一层先异常、资源是否饱和、最近发生了什么变更、止损动作是否有效。Spring Boot 提供 Actuator 和 Micrometer,但它们只是观测入口;完整体系还需要日志平台、指标平台、链路追踪、告警、操作审计和故障复盘。

本文提供可以直接用于生产和值班面试的 Runbook。命令只是示例,执行前必须确认环境、权限、版本和影响范围。

一、先记住排查主线

mermaid
flowchart TD
    A["收到告警或用户反馈"] --> B["确认影响范围和开始时间"]
    B --> C["冻结变更并保存现场"]
    C --> D["看流量、错误率、耗时、饱和度"]
    D --> E{"单实例还是全实例"}
    E -- "单实例" --> F["JVM、线程、容器、宿主机"]
    E -- "全实例" --> G["数据库、Redis、MQ、下游、网络"]
    F --> H["日志 + 指标 + Trace + Dump交叉验证"]
    G --> H
    H --> I["限流、降级、摘流量、回滚或扩容"]
    I --> J["验证恢复并持续观察"]
    J --> K["定位根因、修复、补监控和复盘"]

面试时可以浓缩成一句话:

先定影响面和时间窗,再用 RED/USE 指标判断应用层还是资源层;用 traceId 把网关、应用、数据库和下游串起来;必要时采集线程栈、堆和 GC 证据;先做可回退止损,再验证恢复,最后完成根因修复与复盘。

二、监控对象和四类信号

2.1 RED:面向请求

信号含义Spring Boot 常见来源重点拆分维度
Rate每秒请求量、业务吞吐http.server.requests、网关指标服务、路由、实例
Errors失败数和失败率HTTP 状态码、业务错误码、异常日志URI 模板、异常类型、下游
DurationP50/P95/P99 延迟Timer、Trace SpanURI 模板、实例、下游调用

不要只盯平均耗时。少量极慢请求可能被平均值掩盖,而用户体验通常更接近 P95/P99。

2.2 USE:面向资源

信号含义示例
Utilization使用率CPU 使用率、连接池 active/maximum
Saturation饱和与排队CPU run queue、线程池队列、Hikari pending
Errors资源错误OOM、磁盘 IO 错误、连接创建失败

CPU 80% 不一定有问题;CPU 60% 但运行队列持续很长、容器被限流,同样可能导致严重延迟。

2.3 JVM 四组核心指标

维度要看什么异常信号
Heapused、committed、max、老年代趋势Full GC 后基线仍持续抬升
GC次数、暂停时间、分配速率停顿突增、GC 后回收很少
Threadlive、daemon、peak、blocked线程持续增长、大量 BLOCKED/WAITING
Classloaded、unloaded动态生成类异常增长、Metaspace 压力

2.4 业务信号

技术指标正常不代表业务正常。至少监控:

系统业务指标示例
订单创建成功率、支付回调成功率、超时关闭积压
库存扣减失败率、库存不一致数、补偿任务积压
采集采集成功率、医院/设备维度失败率、队列长度
结算对账差异数、结算延迟、重复处理数

业务指标 Tag 必须低基数。订单号、用户 ID、手机号等应进入日志或 Trace 属性,不能成为 Prometheus Label。

三、生产可观测架构

mermaid
flowchart LR
    A["Spring Boot应用"] --> B["Actuator + Micrometer"]
    A --> C["结构化日志"]
    A --> D["Observation / Trace"]
    B --> E["Prometheus"]
    E --> F["Grafana"]
    E --> G["Alertmanager"]
    C --> H["ELK或Loki"]
    D --> I["OTel Collector"]
    I --> J["Tempo或Jaeger"]
    G --> K["值班通知"]
    F --> L["看趋势和实例差异"]
    H --> M["按traceId检索事件"]
    J --> N["还原跨服务耗时"]

三类数据职责不同:

数据擅长回答不擅长回答
Metrics是否异常、何时开始、趋势、哪些实例异常单个请求具体经历了什么
Logs发生了什么事件、参数摘要、异常栈全局趋势和分位数成本较高
Traces一次请求经过哪些服务、哪段最慢长期聚合和所有离散业务事件

正确方法是先用指标发现异常时间窗和实例,再用 Trace 找慢 Span,最后用 traceId 到日志平台读取上下文。

四、Spring Boot 接入基线

4.1 依赖

xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>

Spring Boot 3 的统一观测主线是 Micrometer Observation 和 Micrometer Tracing。Boot 2.x 项目常见 Spring Cloud Sleuth;迁移到 Boot 3 时不能继续照搬旧 Sleuth 配置,要按 Boot、Spring Cloud 和 Tracing Bridge 的精确版本验证。

4.2 最小安全配置

yaml
management:
  server:
    port: 9090
  endpoints:
    web:
      exposure:
        include: health,info,metrics,prometheus
  endpoint:
    health:
      probes:
        enabled: true
      show-details: never
  metrics:
    tags:
      application: ${spring.application.name}

生产原则:

  1. 管理端口只允许管理网络或 Sidecar 访问。
  2. 公网网关不能转发 Actuator。
  3. /env/configprops/beans/loggers/threaddump/heapdump 按需临时开放并鉴权。
  4. show-details 不向匿名用户暴露依赖地址和异常信息。
  5. Heap Dump 优先通过受审计的运维通道采集,不通过 Web 端点下载。

4.3 端点排查表

端点能回答什么生产注意事项
/actuator/health实例和依赖是否健康区分 liveness/readiness,不做重查询
/actuator/prometheusPrometheus 格式指标仅监控网络访问
/actuator/metrics指标名称和具体 Meter适合临时定位,不替代时序平台
/actuator/conditions自动配置为何匹配或不匹配可能暴露内部类和配置结构
/actuator/env最终属性和值来源必须脱敏、鉴权
/actuator/configprops配置对象绑定结果必须脱敏、鉴权
/actuator/threaddump当前线程状态和栈一次快照不能证明趋势
/actuator/loggers查询或临时修改日志级别修改要审计并定时恢复
/actuator/startup启动步骤耗时需要应用启动过程支持对应缓冲记录

更完整的端点、自定义 HealthIndicator 和 Meter 示例见 Actuator监控

五、日志必须做到可检索、可关联

5.1 一条请求日志至少包含

字段作用
timestamp对齐故障时间线,必须明确时区
level控制检索和告警
serviceinstance判断服务和单实例问题
traceIdspanId关联跨服务 Trace
requestId网关或调用方请求标识
uriTemplatemethod聚合同类接口,避免真实 ID 造成高基数
durationMsstatus识别慢请求和错误
errorCodeexception区分业务失败和系统异常
version关联发布版本

不要记录密码、Token、Cookie、身份证、手机号、银行卡、医疗隐私或完整请求体。确需排查时也应按字段白名单脱敏。

5.2 traceId 传播

  • HTTP:使用标准追踪 Header 或公司统一 Header,由框架自动传播优先。
  • MQ:在消息 Header 携带追踪上下文,消费端创建新的 Span。
  • 异步线程:使用框架提供的上下文传播能力;只把 MDC 放进主线程会在异步任务中丢失。
  • 定时任务:没有上游请求,应为每次任务创建新的观测上下文。

不要在业务代码中到处手写随机 traceId;否则无法正确形成父子 Span,也容易在线程复用时忘记清理 MDC。

5.3 临时提高日志级别

bash
curl -X POST http://127.0.0.1:9090/actuator/loggers/com.example.order \
  -H 'Content-Type: application/json' \
  -d '{"configuredLevel":"DEBUG"}'

排查结束恢复:

bash
curl -X POST http://127.0.0.1:9090/actuator/loggers/com.example.order \
  -H 'Content-Type: application/json' \
  -d '{"configuredLevel":"INFO"}'

动态 DEBUG 可能显著增加 CPU、磁盘和敏感信息风险。必须限制包范围、持续时间和实例数量,并记录操作人。

六、看板和告警怎么设计

6.1 最小服务看板

区域图表
流量QPS、入口/出口流量、实例数
质量5xx、业务失败率、超时率
延迟P50、P95、P99,按 URI 模板和下游拆分
JVMHeap、GC 暂停、线程数、类加载
容器CPU、CPU throttling、内存 working set、重启数
线程池active、pool size、queue、reject
数据库Hikari active/idle/pending、获取连接耗时、慢 SQL
Redis命中率、连接数、耗时、超时
MQ生产/消费速率、失败、重试、积压、最老消息年龄
业务核心交易成功率、任务积压和处理延迟

6.2 告警不要只写“超过阈值”

一条可执行告警至少包含:服务、环境、实例、当前值、阈值、持续时间、影响、看板链接、日志链接、Runbook 链接、最近发布版本。

告警示例思路避免的问题
错误率5xx 比例持续升高且请求量达到最低门槛低流量时一次失败造成 100% 噪声
延迟P99 连续多个窗口超阈值单个瞬时尖峰反复报警
连接池pending > 0 且 active 接近 maximum只看 active 造成误报
JVMFull GC 频繁且 GC 后使用量持续高仅凭 Heap 80% 报警
MQ最老消息年龄和积压同时增长业务低峰固定积压误报
实例readiness 失败或重启次数增加只看进程是否存在

阈值必须基于容量测试、历史基线和 SLO 调整,不能把示例数字直接照搬到生产。

七、线上排查标准 Runbook

7.1 第一步:确认现象

先回答:

  1. 是用户反馈、业务监控还是基础设施告警?
  2. 哪个环境、地域、租户、接口、业务功能受影响?
  3. 从什么时候开始,持续还是间歇?
  4. 全量用户还是少量用户?单实例还是全部实例?
  5. 错误、变慢、数据错误还是完全不可用?

不要在影响面未知时直接重启全部实例。重启可能销毁线程栈、临时日志、现场连接和内存证据。

7.2 第二步:建立时间线

时间事件证据
T-30m新版本发布发布平台、镜像版本
T-10mQPS 开始上涨Prometheus/Grafana
T0P99 和连接等待同时升高HTTP、Hikari 指标
T+5m出现数据库超时应用日志、Trace
T+10m限流后恢复网关指标、业务成功率

重点对齐:发布、配置变更、扩缩容、流量变化、定时任务、数据库 DDL、下游故障和证书过期。

7.3 第三步:四层定位

层次先看什么典型问题
入口DNS、CDN、LB、网关状态码和耗时502/504、路由、限流、证书
应用RED、日志、Trace、线程池、JVM异常、锁竞争、队列堆积、GC
依赖DB、Redis、MQ、第三方接口慢 SQL、连接耗尽、积压、超时
资源容器、宿主机、网络、磁盘CPU throttling、OOMKill、IO 满、丢包

7.4 第四步:止损

按风险从低到高考虑:

  1. 暂停非核心任务、批处理或异常流量入口。
  2. 限流、熔断、降级、返回缓存或只读结果。
  3. 摘除单个异常实例,保留现场副本。
  4. 扩容,但先确认瓶颈不是共享数据库或下游。
  5. 回滚最近发布或配置。
  6. 最后才考虑重启;重启前尽可能采集线程栈、GC、日志和必要 Dump。

所有动作都要有回退方案,写明负责人、时间和验证指标。

7.5 第五步:验证恢复

不能只看“接口能访问”。至少确认:

  • 错误率、P95/P99 回到基线。
  • 核心业务成功率恢复。
  • 线程池、连接池、队列不再持续积压。
  • GC、CPU、内存没有继续恶化。
  • 补偿、重试没有制造第二波流量。
  • 持续观察至少覆盖一个有意义的业务窗口。

八、Linux 和进程级证据

以下命令需要相应工具和权限;容器环境应结合 cgroup 指标判断,不能只看宿主机总量。

bash
# 系统负载、内存、运行队列
uptime
free -m
vmstat 1 10

# 进程 CPU、线程和上下文切换
top -H -p <pid>
pidstat -p <pid> 1 10
pidstat -w -p <pid> 1 10

# 磁盘和网络
iostat -xz 1 10
ss -s
ss -antp
lsof -p <pid> | wc -l

# 文件系统
df -h
df -i
du -x -h --max-depth=1 /path/to/check

关键解释:

现象可能方向
load 高、CPU 高计算热点、死循环、大量 GC
load 高、CPU 不高IO 等待、不可中断任务、运行队列
wa 高、磁盘延迟高磁盘瓶颈、日志洪峰、云盘异常
文件描述符接近上限连接泄漏、文件未关闭、上限过低
大量 TIME_WAIT短连接多;先判断是否真造成端口压力
大量 CLOSE_WAIT本端收到关闭但未正确释放 Socket

九、JVM 排查工具箱

优先使用与目标 JVM 同版本、同用户权限的工具。JDK 版本和容器 PID 命名空间不同会导致 attach 失败。

9.1 低风险信息

bash
jcmd <pid> VM.version
jcmd <pid> VM.flags
jcmd <pid> VM.command_line
jcmd <pid> GC.heap_info
jcmd <pid> Thread.print -l
jstat -gcutil <pid> 1000 10

9.2 连续线程栈

bash
jcmd <pid> Thread.print -l > threads-1.txt
# 间隔数秒后再次采集,至少三次
jcmd <pid> Thread.print -l > threads-2.txt
jcmd <pid> Thread.print -l > threads-3.txt

一次线程栈只是一张照片。相同线程在多次快照中停留在同一业务栈,才更能说明阻塞点或热点。

9.3 Dump 和录制的风险

操作用途风险与要求
Thread Dump锁、阻塞、热点栈通常风险较低,但高频采集仍有开销
Heap Histogram对象数量和大对象初筛可能触发较重统计,繁忙实例谨慎
Heap Dump内存泄漏、对象引用链可能暂停应用、产生接近 Heap 大小的文件
JFRCPU、分配、锁、IO 综合分析开销相对可控,仍需按版本和参数评估

Heap Dump 前必须检查剩余磁盘空间、Pod 临时盘限制和敏感数据合规。不要在磁盘即将写满时直接 Dump。

9.4 Arthas 常用路径

text
dashboard
thread
thread -n 5
thread --state BLOCKED
jvm
memory
sc -d com.example.OrderService
watch com.example.OrderService createOrder '{params,returnObj,throwExp}' -x 2
trace com.example.OrderService createOrder '#cost > 100'
tt -t com.example.OrderService createOrder

watchtracett 会增强字节码并采集运行数据。线上必须限制类、方法、条件、展开深度和持续时间;参数或返回值可能包含敏感数据。排查后撤销增强并退出,不能把无条件 trace 长时间挂在高 QPS 方法上。

十、典型故障一:接口突然变慢

排查顺序:

  1. 看 QPS 是否变化,P95/P99 从何时升高。
  2. 按 URI 模板、实例、状态码拆分,判断单接口还是全局。
  3. 看 Trace 的服务端、数据库、Redis、HTTP 客户端 Span。
  4. 看 Tomcat/Jetty 线程、业务线程池、Hikari pending。
  5. 看 CPU、GC、容器 throttling 和网络。
  6. 查慢 SQL、锁等待、下游超时和重试放大。
指标组合更可能的原因
QPS 不变,P99 高,Hikari pending 高慢 SQL、长事务、连接池不足或泄漏
QPS 高,线程池队列持续增长容量不足或下游变慢导致排队
单实例 P99 高,其他正常单实例 GC、热点、网络或坏节点
所有服务同时变慢共享 DB、Redis、网络、网关或基础设施
客户端 504,应用仍在执行超时层级不一致,请求超时后未被取消

十一、典型故障二:CPU 飙高

mermaid
flowchart TD
    A["CPU持续高"] --> B{"GC CPU是否同时高"}
    B -- "是" --> C["看分配速率、老年代、GC日志"]
    B -- "否" --> D["定位高CPU线程"]
    D --> E["top -H -p PID"]
    E --> F["线程ID转十六进制"]
    F --> G["在线程栈中匹配nid"]
    G --> H["多次快照确认热点调用栈"]

Linux 线程 ID 转十六进制:

bash
printf '%x\n' <tid>

然后在线程栈中搜索对应 nid=0x...。常见根因包括死循环、低效正则、超大 JSON、加解密、日志格式化、频繁 GC、自旋锁和异常风暴。

容器中还要检查 CPU limit 和 throttling。应用使用率看似不高,也可能因为额度过小被频繁节流。

十二、典型故障三:内存上涨和 OOM

先区分:

内存可能来源
Java Heap缓存无界、集合持有、消息积压、大对象
Metaspace类加载器泄漏、动态生成类过多
Direct MemoryNIO/Netty 直接内存、Buffer 未及时释放
Thread Stack线程过多、每线程栈较大
Native/AgentJNI、监控 Agent、压缩库等
Page Cache文件 IO,由 OS 管理,不等同于 Heap 泄漏

判断 Heap 泄漏不能只看 used 上升,应观察多次 Full GC 后的存活基线是否持续抬升。推荐证据链:

  1. 内存和 GC 趋势。
  2. GC 日志或 JFR 的分配热点。
  3. Class Histogram 对比。
  4. 必要时 Heap Dump,用 MAT 等分析 Dominator Tree、Retained Size 和到 GC Root 的路径。
  5. 验证是合理缓存、流量增长还是不可释放引用。

发生 OOMKill 但没有 Java OutOfMemoryError 时,优先检查容器内存上限、工作集、退出码和平台事件;进程可能被操作系统直接杀死,来不及打印 Java 异常。

十三、典型故障四:线程池耗尽、死锁和请求堆积

13.1 线程状态不是根因结论

状态含义常见场景
RUNNABLE可运行,也可能在本地 IO计算、Socket 读写
BLOCKED等待进入 synchronized锁竞争
WAITING无限等待另一个动作park、队列获取、join
TIMED_WAITING有超时等待sleep、带超时的 park/IO

大量 WAITING 线程可能只是正常线程池空闲;必须结合栈、队列长度、active 数和请求延迟判断。

13.2 线程池指标

指标说明
core/max/pool size配置与当前线程数
active正在执行任务数
queue size/capacity排队和容量
completed完成速率
rejected拒绝次数

无界队列会把压力转化为内存和延迟,过大的线程池会增加上下文切换并压垮下游。容量需要根据任务的 CPU/IO 比例、下游容量和压测结果确定。

13.3 死锁

jcmd <pid> Thread.print -ljstack -l <pid> 通常会报告 Java 监视器死锁。修复方向包括统一加锁顺序、缩小锁范围、避免持锁调用远程接口、使用超时锁并设计失败处理。

十四、典型故障五:数据库连接池耗尽

关注 Hikari:

  • active、idle、max。
  • pending threads。
  • connection acquire time。
  • timeout 次数。
  • 数据库端连接数、运行 SQL、锁等待和慢查询。
证据可能原因处理方向
active 接近 max,pending 增长,慢 SQL 增多SQL 或数据库变慢索引、执行计划、锁、数据库资源
active 长期不下降,线程栈停在业务事务长事务缩小事务边界,禁止事务内慢远程调用
泄漏检测提示连接未归还连接泄漏检查手工 JDBC、流式查询和异常路径
扩大池后 DB 更慢数据库已饱和恢复池大小、限流、优化 DB
获取连接慢但 SQL 快池太小或请求并发过高结合 DB 容量调整池和入口并发

连接池不是越大越好。所有实例的池上限之和必须小于数据库可承受连接数,并为管理、迁移和其他服务留余量。

十五、典型故障六:慢 SQL、锁等待和事务问题

应用侧先拿到:SQL 模板、耗时、调用入口、事务范围、连接等待时间。数据库侧再确认:

  1. 是否使用预期索引,估算行数与实际行数差多少。
  2. 是否出现全表扫描、临时表、排序和回表过多。
  3. 是否存在行锁、间隙锁、元数据锁等待。
  4. 事务是否长期未提交,是否包含远程调用。
  5. 数据量、参数分布、统计信息是否变化。

不要在线上日志输出拼接后的完整敏感 SQL;使用 SQL 模板、耗时、影响行数和安全的参数摘要。EXPLAIN ANALYZE 会实际执行查询,不能不评估成本就在生产对写语句或大查询使用。

十六、典型故障七:Redis、MQ 和下游接口

16.1 Redis

现象排查
超时升高Redis CPU、慢命令、网络、连接池等待、大 Key
命中率下降Key 过期、缓存穿透、版本或前缀变化
应用线程堆积客户端连接池、超时和重试配置
内存突增Key 数量、大 Key、淘汰策略、TTL

禁止在大实例生产环境随意执行阻塞式全量命令。Key 分析优先使用渐进扫描、采样和平台工具。

16.2 MQ

指标解释
生产/消费速率判断入口增长还是消费下降
Lag/积压量当前欠账
最老消息年龄用户实际等待时间,通常比单看数量更重要
重试和死信业务失败或毒消息
消费耗时DB、下游或业务处理变慢

扩容消费者前确认分区/队列并行度和下游容量。否则消费者增加也没有并行度,或会把数据库进一步压垮。

16.3 HTTP 下游

同时看连接建立、连接池等待、TLS、首字节、读取耗时、状态码和重试。超时必须分层设计:连接超时、读取超时、整体调用超时、入口超时之间要留出处理和降级时间。

重试只适合可安全重试的操作,并必须有次数、退避、抖动和总时限;无界重试会制造流量放大。

十七、典型故障八:502、504、404 和连接拒绝

状态/现象常见含义排查方向
502网关无法获得有效上游响应实例退出、连接重置、协议错误、路由
504网关等待上游超时应用慢、下游慢、超时层级不一致
404路由或应用映射不存在context-path、网关 Rewrite、版本发布
Connection refused目标端口无监听或被拒绝进程、端口、readiness、网络策略
Connection reset对端或中间设备关闭连接重启、超时、协议、负载均衡

确认错误由哪一层生成非常重要。网关返回的 504 不等于 Spring Boot 返回了 504;应用可能仍在后台执行并最终写数据库,引发客户端重试和重复业务。

十八、Kubernetes 和容器排查

bash
kubectl get pod -n <ns> -o wide
kubectl describe pod <pod> -n <ns>
kubectl logs <pod> -n <ns> --since=30m
kubectl logs <pod> -n <ns> --previous
kubectl top pod <pod> -n <ns> --containers
kubectl get events -n <ns> --sort-by=.lastTimestamp
kubectl get deploy <deploy> -n <ns> -o yaml
kubectl rollout history deploy/<deploy> -n <ns>

重点判断:

现象证据
OOMKilledPod last state、退出码、容器内存曲线
CrashLoopBackOff当前日志、--previous、探针和启动命令
readiness 失败探针响应、应用启动阶段、依赖健康
liveness 反复重启探针是否过重、超时过短、GC 停顿
CPU throttlingCPU limit 与 throttled 指标
Evicted节点内存/磁盘压力和事件

不要把 liveness 配成数据库深度检查。数据库短暂异常若触发所有应用重启,可能形成重连风暴;依赖不可用通常更适合影响 readiness,让实例摘流量但保留诊断现场。

十九、启动失败和启动慢

19.1 启动失败

最后阶段排查入口
配置加载前JVM 参数、命令行、Jar、Class 版本
EnvironmentProfile、配置中心、占位符、解密
Bean 创建最内层 Caused by、循环依赖、条件配置
WebServer端口占用、证书、Connector 配置
Runner外部连接、初始化任务、阻塞逻辑

自动配置问题查看 Condition Evaluation Report、/conditions/env/configprops。不要只搜索异常第一行,要找到最内层根因和第一个业务栈。

19.2 启动慢

检查配置中心和 DNS、数据库连接、类路径扫描、Bean 初始化、@PostConstruct、数据迁移、Runner、远程预热和 ApplicationStartup 记录。初始化逻辑应设置超时,非关键预热不要无限阻塞 readiness。

二十、发布后异常怎么判断

发布前后对比:

  • 应用版本、配置版本、JDK、基础镜像。
  • 流量和实例数量是否一致。
  • 错误率、延迟、CPU、内存和 GC。
  • 数据库连接、SQL 模板和下游调用。
  • 新旧版本是否同时存在,异常是否只出现在新版本。

金丝雀实例异常而旧实例正常,是非常强的版本相关证据,但仍要排除节点、流量标签和数据分片差异。回滚后恢复说明变更与故障相关,不自动证明具体代码根因。

二十一、线上操作红线

高风险动作风险安全做法
全量重启丢现场、流量雪崩单实例摘流量、留现场、滚动处理
全实例 DEBUGCPU 和磁盘暴涨、泄密单包、单实例、短时、有审计
随意 Heap DumpSTW、磁盘写满查空间、评估暂停、走安全通道
无条件 Arthas trace高 QPS 下开销巨大加方法和耗时条件,短时观察
盲目扩大连接池压垮数据库先确认 DB 容量和慢 SQL
盲目扩容应用放大共享依赖压力确认瓶颈层后再扩
直接改生产数据数据不可逆审批、备份、影响行预览、事务和回滚方案
删除日志腾空间丢失证据先归档,修复日志洪峰和轮转策略

二十二、故障复盘模板

部分必须回答
摘要何时发生、持续多久、影响什么
影响用户数、订单数、SLO、数据一致性
时间线告警、响应、判断、操作、恢复时间
根因技术根因和触发条件,证据是什么
放大因素重试、缺少限流、告警延迟、操作失误
止损评价哪个动作有效,哪个动作无效或有副作用
修复代码、配置、容量、架构和数据修复
防复发测试、监控、告警、Runbook、演练
负责人每项行动、截止时间和验收方式

复盘不以“某个人写错代码”为结论,而要追问为什么测试、评审、灰度、监控和保护机制都没有更早阻断。

二十三、面试高频题与回答主线

23.1 线上接口突然变慢怎么排查

先确认时间窗、影响接口和实例范围;看 QPS、错误率、P95/P99 判断是否流量变化。再按实例拆分 CPU、GC、线程池和连接池,通过 Trace 判断耗时在应用、SQL、Redis 还是下游。若线程池排队或 Hikari pending 升高,结合连续线程栈、慢 SQL和数据库锁等待验证。止损优先限流、降级、摘异常实例或回滚,恢复后继续观察核心业务和资源指标。

23.2 CPU 100% 怎么排查

先判断进程 CPU、容器 throttling 和 GC CPU。若不是 GC,用 top -H -p 找高 CPU 线程,将线程 ID 转十六进制后匹配连续线程栈;也可用 JFR 或受控 Arthas 定位热点方法。常见原因是死循环、低效计算、异常风暴、日志和高分配率。不能只凭一次线程栈下结论。

23.3 内存持续上涨就是泄漏吗

不一定。可能是流量导致 Heap 扩容、合理缓存、直接内存、线程栈或 Page Cache。Heap 泄漏要看 Full GC 后存活基线是否持续上涨,再结合 GC 日志、Histogram、JFR 分配热点和必要的 Heap Dump 引用链证明。

23.4 为什么 Actuator 不是完整监控系统

Actuator 提供健康、指标和诊断端点,Micrometer 提供观测门面,但不负责长期存储、可视化、聚合告警和跨服务检索。生产还需要 Prometheus/Grafana、日志平台、Trace 后端和告警值班体系。

23.5 Metrics、Logs、Traces 如何配合

Metrics 用于发现异常趋势和范围;Trace 还原单次跨服务调用并定位慢 Span;Logs 提供异常栈和业务事件上下文。通常先由告警确定时间窗和实例,再查 Trace,最后通过 traceId 定位日志。

23.6 为什么不能看到服务异常就重启

重启可能暂时恢复,但会丢失线程、连接和内存现场,掩盖根因;全量重启还可能造成流量抖动和依赖重连风暴。应先确认影响并采集低风险证据,优先摘除单实例、限流、降级或回滚,确需重启时滚动执行并验证。

二十四、一页纸值班清单

收到告警

  • [ ] 确认环境、服务、接口、实例、开始时间和影响范围。
  • [ ] 查看最近发布、配置、扩缩容、DDL 和定时任务。
  • [ ] 保存告警、看板、日志和 Trace 链接。
  • [ ] 指定现场负责人、操作人和同步渠道。

定位

  • [ ] 看 Rate、Errors、Duration 和资源 Saturation。
  • [ ] 判断单实例、单接口还是共享依赖。
  • [ ] 用 traceId 关联网关、应用和下游。
  • [ ] 必要时采集三次线程栈、GC/JFR 或受控 Dump。
  • [ ] 所有判断写出支持证据和反证。

止损与恢复

  • [ ] 选择可回退的限流、降级、摘流量、扩容或回滚。
  • [ ] 记录操作时间、对象、参数和执行人。
  • [ ] 验证错误率、P99、业务成功率和积压恢复。
  • [ ] 防止重试、补偿任务产生第二波冲击。

事后

  • [ ] 修复根因,不把重启当修复。
  • [ ] 补测试、容量基线、监控、告警和 Runbook。
  • [ ] 完成数据补偿和影响确认。
  • [ ] 复盘行动项有负责人、期限和验收标准。

二十五、关联学习

专题继续学习什么
Actuator监控端点、HealthIndicator、Micrometer 和 Prometheus 接入代码
Spring Boot Admin多实例Actuator集中展示、注册发现、认证和通知
从零到生产级掌握Spring Boot 完整工程主线
配置体系全过程Profile、属性来源、绑定失败和最终值排查
自动配置与扩展链conditions、Bean 和自动装配诊断
Java 17+与Boot 3Observation、Tracing 和版本迁移
JVM性能调优GC、线程、内存和 JVM 参数
MySQL监控与故障排查慢 SQL、锁、连接和数据库资源

本章小结

生产监控的目标不是堆工具,而是缩短发现、定位和恢复时间。一个成熟的 Spring Boot 服务应该能够用指标发现异常、用 Trace 确定慢在哪一段、用日志解释发生了什么,并通过线程栈、GC、数据库和容器证据完成验证。真正专业的线上排查必须同时做到三点:保留证据、控制操作风险、用业务指标证明恢复。