摘要: 生产环境的故障往往来自意想不到的角落。本文收录了10个真实的SpringBoot生产故障案例,涵盖OOM内存溢出、线程池耗尽、数据库连接泄漏、缓存雪崩、死锁、配置错误等典型问题。每个案例都包含完整的故障现象、排查过程、根因分析和解决方案,帮助开发者提前规避风险,快速定位问题。适合1-5年经验的Java后端开发者和运维工程师阅读。
时效性说明: 本文基于 SpringBoot 3.x + JDK 17 生产环境实际案例整理(2026年6月)。故障排查方法论和解决方案具有通用性,但具体配置参数会随版本调整,使用时请以实际环境为准。
运行环境: SpringBoot 3.x、JDK 17+、MySQL 8.0+、Redis 7.0+
场景化开篇
你是否经历过这些生产事故?
场景一:半夜被电话叫醒
- 凌晨3点,收到告警短信:服务不可用
- 起床打开电脑,发现OOM导致服务崩溃
- 紧急重启服务,但很快又崩溃
- 排查2小时才发现是内存泄漏
- 运维OS:能不能提前发现问题?
场景二:大促期间系统崩溃
- 双11零点,流量激增10倍
- 线程池瞬间耗尽,大量请求超时
- 用户投诉不断,订单流失严重
- 紧急扩容,但来不及了
- 技术负责人OS:为什么压测没发现?
场景三:数据库连接泄漏
- 系统运行一段时间后,数据库连接耗尽
- 新请求无法获取连接,报错超时
- 重启服务后恢复,但过几天又出现
- 排查发现是连接未正确关闭
- 开发OS:代码明明有关闭连接啊?
场景四:配置错误导致故障
- 修改了一个配置参数,重新部署
- 服务启动成功,但功能异常
- 回滚配置后恢复,但不知道原因
- 架构师OS:配置变更怎么测试?
生产故障成本分析(按电商场景计算)
故障影响统计:
| 故障类型 | 平均恢复时间 | 直接损失 | 间接损失 | 年度成本 |
|---|---|---|---|---|
| OOM崩溃 | 30分钟 | 5万元 | 品牌损失 | 60万元 |
| 线程池耗尽 | 15分钟 | 2万元 | 用户流失 | 24万元 |
| 连接池泄漏 | 60分钟 | 10万元 | 运维成本 | 120万元 |
| 缓存雪崩 | 45分钟 | 8万元 | 数据修复 | 96万元 |
| 死锁问题 | 30分钟 | 5万元 | 排查成本 | 60万元 |
| 年度总损失 | - | - | - | 360万元 |
预防措施收益:
| 措施 | 投入成本 | 预防效果 | ROI |
|---|---|---|---|
| 监控告警 | 10万元 | 预防80%故障 | 28倍 |
| 代码审查 | 15万元 | 预防60%故障 | 14倍 |
| 压力测试 | 20万元 | 预防70%故障 | 12倍 |
| 配置管理 | 5万元 | 预防90%配置错误 | 64倍 |
前言与概述
在前二十九章中,我完成了SpringBoot从环境搭建到分布式事务的完整学习路径。今天我会探讨生产环境的真实挑战------故障案例与教训。生产环境的故障往往来自意想不到的角落,本文收录的10个真实案例,将帮助你提前规避风险,快速定位问题。
故障分类概览
#mermaid-svg-6bOsCDMAUPLhL16U{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6bOsCDMAUPLhL16U .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6bOsCDMAUPLhL16U .error-icon{fill:#552222;}#mermaid-svg-6bOsCDMAUPLhL16U .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6bOsCDMAUPLhL16U .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6bOsCDMAUPLhL16U .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6bOsCDMAUPLhL16U .marker.cross{stroke:#333333;}#mermaid-svg-6bOsCDMAUPLhL16U svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6bOsCDMAUPLhL16U p{margin:0;}#mermaid-svg-6bOsCDMAUPLhL16U .edge{stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .section--1 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section--1 path,#mermaid-svg-6bOsCDMAUPLhL16U .section--1 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section--1 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section--1 text{fill:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth--1{stroke-width:17;}#mermaid-svg-6bOsCDMAUPLhL16U .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-0 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-0 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-0 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-0 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-0 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-0{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-0{stroke-width:14;}#mermaid-svg-6bOsCDMAUPLhL16U .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-1 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-1 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-1 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-1 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-1 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-1{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-1{stroke-width:11;}#mermaid-svg-6bOsCDMAUPLhL16U .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-2 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-2 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-2 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-2 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-2 text{fill:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-2{stroke-width:8;}#mermaid-svg-6bOsCDMAUPLhL16U .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-3 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-3 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-3 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-3 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-3 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-3{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-3{stroke-width:5;}#mermaid-svg-6bOsCDMAUPLhL16U .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-4 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-4 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-4 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-4 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-4 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-4{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-4{stroke-width:2;}#mermaid-svg-6bOsCDMAUPLhL16U .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-5 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-5 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-5 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-5 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-5 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-5{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-5{stroke-width:-1;}#mermaid-svg-6bOsCDMAUPLhL16U .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-6 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-6 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-6 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-6 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-6 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-6{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-6{stroke-width:-4;}#mermaid-svg-6bOsCDMAUPLhL16U .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-7 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-7 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-7 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-7 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-7 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-7{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-7{stroke-width:-7;}#mermaid-svg-6bOsCDMAUPLhL16U .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-8 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-8 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-8 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-8 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-8 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-8{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-8{stroke-width:-10;}#mermaid-svg-6bOsCDMAUPLhL16U .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-9 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-9 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-9 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-9 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-9 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-9{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-9{stroke-width:-13;}#mermaid-svg-6bOsCDMAUPLhL16U .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-10 rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-10 path,#mermaid-svg-6bOsCDMAUPLhL16U .section-10 circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-10 polygon,#mermaid-svg-6bOsCDMAUPLhL16U .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-10 text{fill:black;}#mermaid-svg-6bOsCDMAUPLhL16U .node-icon-10{font-size:40px;color:black;}#mermaid-svg-6bOsCDMAUPLhL16U .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .edge-depth-10{stroke-width:-16;}#mermaid-svg-6bOsCDMAUPLhL16U .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled,#mermaid-svg-6bOsCDMAUPLhL16U .disabled circle,#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:lightgray;}#mermaid-svg-6bOsCDMAUPLhL16U .disabled text{fill:#efefef;}#mermaid-svg-6bOsCDMAUPLhL16U .section-root rect,#mermaid-svg-6bOsCDMAUPLhL16U .section-root path,#mermaid-svg-6bOsCDMAUPLhL16U .section-root circle,#mermaid-svg-6bOsCDMAUPLhL16U .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-6bOsCDMAUPLhL16U .section-root text{fill:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .section-root span{color:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .section-2 span{color:#ffffff;}#mermaid-svg-6bOsCDMAUPLhL16U .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-6bOsCDMAUPLhL16U .edge{fill:none;}#mermaid-svg-6bOsCDMAUPLhL16U .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-6bOsCDMAUPLhL16U :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生产故障
内存问题
OOM堆溢出
OOM元空间
内存泄漏
GC频繁
线程问题
线程池耗尽
死锁
线程泄漏
阻塞等待
数据库问题
连接池泄漏
慢SQL
锁等待
主从延迟
缓存问题
缓存穿透
缓存雪崩
缓存击穿
数据不一致
配置问题
参数错误
环境差异
版本冲突
依赖缺失
第一章:内存问题案例
1.1 案例一:List 无限增长导致 OOM
故障现象:
- 服务运行2-3天后频繁Full GC
- 最终抛出
java.lang.OutOfMemoryError: Java heap space - 重启后恢复正常,但过几天又出现
排查过程:
java
// 问题代码
@Service
public class ReportService {
// 问题:静态集合持续增长,永远不会释放
private static final List<ReportRecord> records = new ArrayList<>();
public void generateReport(ReportRequest request) {
// 每次请求都往 list 中添加数据
ReportRecord record = buildRecord(request);
records.add(record); // 持续增长,永不清理
// 处理报告
processReport(records);
}
}
// 使用 jmap 分析堆内存
// jmap -dump:format=b,file=heap.hprof <pid>
// 使用 MAT 工具分析,发现 ArrayList 占用 2GB 内存
根因分析:
#mermaid-svg-gMJIVRzLtKQhMfcB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-gMJIVRzLtKQhMfcB .error-icon{fill:#552222;}#mermaid-svg-gMJIVRzLtKQhMfcB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-gMJIVRzLtKQhMfcB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-gMJIVRzLtKQhMfcB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-gMJIVRzLtKQhMfcB .marker.cross{stroke:#333333;}#mermaid-svg-gMJIVRzLtKQhMfcB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-gMJIVRzLtKQhMfcB p{margin:0;}#mermaid-svg-gMJIVRzLtKQhMfcB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB .cluster-label text{fill:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB .cluster-label span{color:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB .cluster-label span p{background-color:transparent;}#mermaid-svg-gMJIVRzLtKQhMfcB .label text,#mermaid-svg-gMJIVRzLtKQhMfcB span{fill:#333;color:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB .node rect,#mermaid-svg-gMJIVRzLtKQhMfcB .node circle,#mermaid-svg-gMJIVRzLtKQhMfcB .node ellipse,#mermaid-svg-gMJIVRzLtKQhMfcB .node polygon,#mermaid-svg-gMJIVRzLtKQhMfcB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-gMJIVRzLtKQhMfcB .rough-node .label text,#mermaid-svg-gMJIVRzLtKQhMfcB .node .label text,#mermaid-svg-gMJIVRzLtKQhMfcB .image-shape .label,#mermaid-svg-gMJIVRzLtKQhMfcB .icon-shape .label{text-anchor:middle;}#mermaid-svg-gMJIVRzLtKQhMfcB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-gMJIVRzLtKQhMfcB .rough-node .label,#mermaid-svg-gMJIVRzLtKQhMfcB .node .label,#mermaid-svg-gMJIVRzLtKQhMfcB .image-shape .label,#mermaid-svg-gMJIVRzLtKQhMfcB .icon-shape .label{text-align:center;}#mermaid-svg-gMJIVRzLtKQhMfcB .node.clickable{cursor:pointer;}#mermaid-svg-gMJIVRzLtKQhMfcB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-gMJIVRzLtKQhMfcB .arrowheadPath{fill:#333333;}#mermaid-svg-gMJIVRzLtKQhMfcB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-gMJIVRzLtKQhMfcB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-gMJIVRzLtKQhMfcB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gMJIVRzLtKQhMfcB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-gMJIVRzLtKQhMfcB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gMJIVRzLtKQhMfcB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-gMJIVRzLtKQhMfcB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-gMJIVRzLtKQhMfcB .cluster text{fill:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB .cluster span{color:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-gMJIVRzLtKQhMfcB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-gMJIVRzLtKQhMfcB rect.text{fill:none;stroke-width:0;}#mermaid-svg-gMJIVRzLtKQhMfcB .icon-shape,#mermaid-svg-gMJIVRzLtKQhMfcB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-gMJIVRzLtKQhMfcB .icon-shape p,#mermaid-svg-gMJIVRzLtKQhMfcB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-gMJIVRzLtKQhMfcB .icon-shape .label rect,#mermaid-svg-gMJIVRzLtKQhMfcB .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-gMJIVRzLtKQhMfcB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-gMJIVRzLtKQhMfcB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-gMJIVRzLtKQhMfcB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 请求1
添加记录1
请求2
添加记录2
请求N
添加记录N
List 持续增长
内存耗尽 OOM
解决方案:
java
// 方案一:使用有界集合
@Service
public class FixedReportService {
// 使用固定大小的队列,自动淘汰旧数据
private final Deque<ReportRecord> records = new ArrayDeque<>(10000);
public synchronized void generateReport(ReportRequest request) {
ReportRecord record = buildRecord(request);
records.addLast(record);
// 超过容量限制,移除最旧的数据
while (records.size() > 10000) {
records.removeFirst();
}
processReport(new ArrayList<>(records));
}
}
// 方案二:定期清理
@Service
public class FixedReportService2 {
private final List<ReportRecord> records = new ArrayList<>();
// 每天凌晨2点清理旧数据
@Scheduled(cron = "0 0 2 * * *")
public void cleanup() {
LocalDate yesterday = LocalDate.now().minusDays(1);
records.removeIf(r -> r.getCreatedAt().toLocalDate().isBefore(yesterday));
log.info("清理完成,剩余记录数: {}", records.size());
}
}
1.2 案例二:ThreadLocal 未清理导致内存泄漏
故障现象:
- 每次请求处理后内存不释放
- 长时间运行后元空间溢出
- 报错
java.lang.OutOfMemoryError: Metaspace
排查过程:
java
// 问题代码
public class UserContext {
// 问题:ThreadLocal 使用后未清理
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setUser(User user) {
currentUser.set(user);
}
public static User getUser() {
return currentUser.get();
}
// 缺少 remove() 调用
}
// 在 Filter 中使用
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
User user = extractUser(request);
UserContext.setUser(user);
chain.doFilter(request, response);
// 问题:没有调用 UserContext.remove()
// ThreadLocal 中的 User 对象不会被回收
}
}
根因分析:
GC ThreadLocal 线程池线程 GC ThreadLocal 线程池线程 #mermaid-svg-LKLSm5BHkgIsOZfX{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-LKLSm5BHkgIsOZfX .error-icon{fill:#552222;}#mermaid-svg-LKLSm5BHkgIsOZfX .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-LKLSm5BHkgIsOZfX .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-LKLSm5BHkgIsOZfX .marker{fill:#333333;stroke:#333333;}#mermaid-svg-LKLSm5BHkgIsOZfX .marker.cross{stroke:#333333;}#mermaid-svg-LKLSm5BHkgIsOZfX svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-LKLSm5BHkgIsOZfX p{margin:0;}#mermaid-svg-LKLSm5BHkgIsOZfX .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LKLSm5BHkgIsOZfX text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LKLSm5BHkgIsOZfX .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-LKLSm5BHkgIsOZfX .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-LKLSm5BHkgIsOZfX #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-LKLSm5BHkgIsOZfX .sequenceNumber{fill:white;}#mermaid-svg-LKLSm5BHkgIsOZfX #sequencenumber{fill:#333;}#mermaid-svg-LKLSm5BHkgIsOZfX #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-LKLSm5BHkgIsOZfX .messageText{fill:#333;stroke:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LKLSm5BHkgIsOZfX .labelText,#mermaid-svg-LKLSm5BHkgIsOZfX .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .loopText,#mermaid-svg-LKLSm5BHkgIsOZfX .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-LKLSm5BHkgIsOZfX .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-LKLSm5BHkgIsOZfX .noteText,#mermaid-svg-LKLSm5BHkgIsOZfX .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-LKLSm5BHkgIsOZfX .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LKLSm5BHkgIsOZfX .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LKLSm5BHkgIsOZfX .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-LKLSm5BHkgIsOZfX .actorPopupMenu{position:absolute;}#mermaid-svg-LKLSm5BHkgIsOZfX .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-LKLSm5BHkgIsOZfX .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-LKLSm5BHkgIsOZfX .actor-man circle,#mermaid-svg-LKLSm5BHkgIsOZfX line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-LKLSm5BHkgIsOZfX :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} User 对象被引用 ThreadLocal 未清理 无法回收 User 对象 线程复用导致内存泄漏 set(User)处理请求返回线程池
解决方案:
java
// 正确的 ThreadLocal 使用方式
public class UserContext {
private static final ThreadLocal<User> currentUser = new ThreadLocal<>();
public static void setUser(User user) {
currentUser.set(user);
}
public static User getUser() {
return currentUser.get();
}
// 必须在 finally 中清理
public static void clear() {
currentUser.remove();
}
}
// 在 Filter 中正确清理
public class AuthFilter implements Filter {
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) {
try {
User user = extractUser(request);
UserContext.setUser(user);
chain.doFilter(request, response);
} finally {
UserContext.clear(); // 必须清理
}
}
}
第二章:线程问题案例
2.1 案例三:线程池配置不当导致请求拒绝
故障现象:
- 高并发时大量请求被拒绝
- 报错
java.util.concurrent.RejectedExecutionException - 但CPU使用率只有30%
排查过程:
java
// 问题代码
@Configuration
public class ThreadPoolConfig {
@Bean
public ExecutorService taskExecutor() {
// 问题:线程池太小,队列太短
return new ThreadPoolExecutor(
5, // 核心线程数
10, // 最大线程数
60, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 队列容量100
new ThreadPoolExecutor.AbortPolicy() // 直接拒绝
);
}
}
// 并发1000时的情况:
// - 5个核心线程处理任务
// - 100个任务进入队列等待
// - 895个任务被拒绝
根因分析:
#mermaid-svg-dAuwhJ1yCGvxU92k{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dAuwhJ1yCGvxU92k .error-icon{fill:#552222;}#mermaid-svg-dAuwhJ1yCGvxU92k .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dAuwhJ1yCGvxU92k .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dAuwhJ1yCGvxU92k .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dAuwhJ1yCGvxU92k .marker.cross{stroke:#333333;}#mermaid-svg-dAuwhJ1yCGvxU92k svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dAuwhJ1yCGvxU92k p{margin:0;}#mermaid-svg-dAuwhJ1yCGvxU92k .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k .cluster-label text{fill:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k .cluster-label span{color:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k .cluster-label span p{background-color:transparent;}#mermaid-svg-dAuwhJ1yCGvxU92k .label text,#mermaid-svg-dAuwhJ1yCGvxU92k span{fill:#333;color:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k .node rect,#mermaid-svg-dAuwhJ1yCGvxU92k .node circle,#mermaid-svg-dAuwhJ1yCGvxU92k .node ellipse,#mermaid-svg-dAuwhJ1yCGvxU92k .node polygon,#mermaid-svg-dAuwhJ1yCGvxU92k .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dAuwhJ1yCGvxU92k .rough-node .label text,#mermaid-svg-dAuwhJ1yCGvxU92k .node .label text,#mermaid-svg-dAuwhJ1yCGvxU92k .image-shape .label,#mermaid-svg-dAuwhJ1yCGvxU92k .icon-shape .label{text-anchor:middle;}#mermaid-svg-dAuwhJ1yCGvxU92k .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dAuwhJ1yCGvxU92k .rough-node .label,#mermaid-svg-dAuwhJ1yCGvxU92k .node .label,#mermaid-svg-dAuwhJ1yCGvxU92k .image-shape .label,#mermaid-svg-dAuwhJ1yCGvxU92k .icon-shape .label{text-align:center;}#mermaid-svg-dAuwhJ1yCGvxU92k .node.clickable{cursor:pointer;}#mermaid-svg-dAuwhJ1yCGvxU92k .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dAuwhJ1yCGvxU92k .arrowheadPath{fill:#333333;}#mermaid-svg-dAuwhJ1yCGvxU92k .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dAuwhJ1yCGvxU92k .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dAuwhJ1yCGvxU92k .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dAuwhJ1yCGvxU92k .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dAuwhJ1yCGvxU92k .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dAuwhJ1yCGvxU92k .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dAuwhJ1yCGvxU92k .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dAuwhJ1yCGvxU92k .cluster text{fill:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k .cluster span{color:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dAuwhJ1yCGvxU92k .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dAuwhJ1yCGvxU92k rect.text{fill:none;stroke-width:0;}#mermaid-svg-dAuwhJ1yCGvxU92k .icon-shape,#mermaid-svg-dAuwhJ1yCGvxU92k .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dAuwhJ1yCGvxU92k .icon-shape p,#mermaid-svg-dAuwhJ1yCGvxU92k .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dAuwhJ1yCGvxU92k .icon-shape .label rect,#mermaid-svg-dAuwhJ1yCGvxU92k .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dAuwhJ1yCGvxU92k .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dAuwhJ1yCGvxU92k .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dAuwhJ1yCGvxU92k :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 1000个请求
5个核心线程
100个队列等待
895个请求被拒绝
解决方案:
java
// 方案一:调整线程池参数
@Configuration
public class FixedThreadPoolConfig {
@Bean
public ExecutorService taskExecutor() {
// 根据业务特点调整参数
int cpuCores = Runtime.getRuntime().availableProcessors();
int ioRatio = 90; // IO密集型,IO时间占比90%
// 计算最优线程数
int optimalThreads = cpuCores * (1 + ioRatio / (100 - ioRatio));
return new ThreadPoolExecutor(
optimalThreads, // 核心线程数
optimalThreads * 2, // 最大线程数
60, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(10000), // 更大的队列
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者执行策略
);
}
}
// 方案二:使用动态线程池
@Configuration
public class DynamicThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(1000);
executor.setKeepAliveSeconds(60);
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 开启动态调整
executor.setAllowCoreThreadTimeOut(true);
return executor;
}
}
2.2 案例四:数据库死锁导致系统卡死
故障现象:
- 系统响应突然变慢,大量请求超时
- 数据库CPU使用率飙升
- 日志出现
Lock wait timeout exceeded
排查过程:
java
// 问题代码:事务嵌套导致死锁
@Service
public class OrderService {
@Transactional
public void createOrder(OrderRequest request) {
// 事务1:锁住订单表
orderMapper.insert(order);
// 调用库存服务(新事务)
storageService.deduct(request.getProductId(), request.getQuantity());
}
}
@Service
public class StorageService {
@Transactional
public void deduct(Long productId, Integer quantity) {
// 事务2:锁住库存表
storageMapper.deduct(productId, quantity);
// 调用订单服务(又获取订单表的锁)
orderService.updateStatus(orderId, "DEDUCTED");
}
}
// 死锁场景:
// 线程A:持有订单表锁 -> 等待库存表锁
// 线程B:持有库存表锁 -> 等待订单表锁
根因分析:
#mermaid-svg-pLPIhrpQf5CpT25g{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-pLPIhrpQf5CpT25g .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-pLPIhrpQf5CpT25g .error-icon{fill:#552222;}#mermaid-svg-pLPIhrpQf5CpT25g .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-pLPIhrpQf5CpT25g .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-pLPIhrpQf5CpT25g .marker{fill:#333333;stroke:#333333;}#mermaid-svg-pLPIhrpQf5CpT25g .marker.cross{stroke:#333333;}#mermaid-svg-pLPIhrpQf5CpT25g svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-pLPIhrpQf5CpT25g p{margin:0;}#mermaid-svg-pLPIhrpQf5CpT25g .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-pLPIhrpQf5CpT25g .cluster-label text{fill:#333;}#mermaid-svg-pLPIhrpQf5CpT25g .cluster-label span{color:#333;}#mermaid-svg-pLPIhrpQf5CpT25g .cluster-label span p{background-color:transparent;}#mermaid-svg-pLPIhrpQf5CpT25g .label text,#mermaid-svg-pLPIhrpQf5CpT25g span{fill:#333;color:#333;}#mermaid-svg-pLPIhrpQf5CpT25g .node rect,#mermaid-svg-pLPIhrpQf5CpT25g .node circle,#mermaid-svg-pLPIhrpQf5CpT25g .node ellipse,#mermaid-svg-pLPIhrpQf5CpT25g .node polygon,#mermaid-svg-pLPIhrpQf5CpT25g .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-pLPIhrpQf5CpT25g .rough-node .label text,#mermaid-svg-pLPIhrpQf5CpT25g .node .label text,#mermaid-svg-pLPIhrpQf5CpT25g .image-shape .label,#mermaid-svg-pLPIhrpQf5CpT25g .icon-shape .label{text-anchor:middle;}#mermaid-svg-pLPIhrpQf5CpT25g .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-pLPIhrpQf5CpT25g .rough-node .label,#mermaid-svg-pLPIhrpQf5CpT25g .node .label,#mermaid-svg-pLPIhrpQf5CpT25g .image-shape .label,#mermaid-svg-pLPIhrpQf5CpT25g .icon-shape .label{text-align:center;}#mermaid-svg-pLPIhrpQf5CpT25g .node.clickable{cursor:pointer;}#mermaid-svg-pLPIhrpQf5CpT25g .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-pLPIhrpQf5CpT25g .arrowheadPath{fill:#333333;}#mermaid-svg-pLPIhrpQf5CpT25g .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-pLPIhrpQf5CpT25g .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-pLPIhrpQf5CpT25g .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pLPIhrpQf5CpT25g .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-pLPIhrpQf5CpT25g .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pLPIhrpQf5CpT25g .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-pLPIhrpQf5CpT25g .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-pLPIhrpQf5CpT25g .cluster text{fill:#333;}#mermaid-svg-pLPIhrpQf5CpT25g .cluster span{color:#333;}#mermaid-svg-pLPIhrpQf5CpT25g div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-pLPIhrpQf5CpT25g .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-pLPIhrpQf5CpT25g rect.text{fill:none;stroke-width:0;}#mermaid-svg-pLPIhrpQf5CpT25g .icon-shape,#mermaid-svg-pLPIhrpQf5CpT25g .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-pLPIhrpQf5CpT25g .icon-shape p,#mermaid-svg-pLPIhrpQf5CpT25g .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-pLPIhrpQf5CpT25g .icon-shape .label rect,#mermaid-svg-pLPIhrpQf5CpT25g .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-pLPIhrpQf5CpT25g .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-pLPIhrpQf5CpT25g .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-pLPIhrpQf5CpT25g :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 线程B
线程A
等待
等待
获取订单锁
等待库存锁
获取库存锁
等待订单锁
解决方案:
java
// 方案一:统一锁顺序
@Service
public class FixedOrderService {
@Autowired
private StorageService storageService;
@Transactional
public void createOrder(OrderRequest request) {
// 统一按ID顺序获取锁
Long smallerId = Math.min(orderId, productId);
Long largerId = Math.max(orderId, productId);
// 先锁较小的ID
lock(smallerId);
lock(largerId);
try {
orderMapper.insert(order);
storageService.deduct(productId, quantity);
} finally {
unlock(largerId);
unlock(smallerId);
}
}
}
// 方案二:减少事务范围
@Service
public class FixedOrderService2 {
@Transactional
public void createOrder(OrderRequest request) {
// 只在必要的地方使用事务
orderMapper.insert(order);
}
// 库存扣减放在事务外
public void deductStorage(Long productId, Integer quantity) {
storageService.deduct(productId, quantity);
}
}
第三章:数据库问题案例
3.1 案例五:连接池泄漏导致服务不可用
故障现象:
- 服务运行一段时间后报错
Cannot get a connection, pool error Timeout waiting for idle object - 重启后恢复,但过几天又出现
- 监控显示活跃连接数持续增长
排查过程:
java
// 问题代码:连接未正确关闭
@Repository
public class ProblematicUserMapper {
@Autowired
private DataSource dataSource;
public User findById(Long id) {
Connection conn = null;
PreparedStatement ps = null;
ResultSet rs = null;
try {
conn = dataSource.getConnection();
ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
ps.setLong(1, id);
rs = ps.executeQuery();
if (rs.next()) {
return mapUser(rs);
}
return null;
} catch (SQLException e) {
// 问题:异常时连接未关闭
throw new RuntimeException(e);
}
// 缺少 finally 块关闭连接
}
}
根因分析:
数据库 连接池 应用 数据库 连接池 应用 #mermaid-svg-wVuNwdh9RrpjtYf4{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-wVuNwdh9RrpjtYf4 .error-icon{fill:#552222;}#mermaid-svg-wVuNwdh9RrpjtYf4 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-wVuNwdh9RrpjtYf4 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-wVuNwdh9RrpjtYf4 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-wVuNwdh9RrpjtYf4 .marker.cross{stroke:#333333;}#mermaid-svg-wVuNwdh9RrpjtYf4 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-wVuNwdh9RrpjtYf4 p{margin:0;}#mermaid-svg-wVuNwdh9RrpjtYf4 .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wVuNwdh9RrpjtYf4 text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wVuNwdh9RrpjtYf4 .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-wVuNwdh9RrpjtYf4 .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-wVuNwdh9RrpjtYf4 #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-wVuNwdh9RrpjtYf4 .sequenceNumber{fill:white;}#mermaid-svg-wVuNwdh9RrpjtYf4 #sequencenumber{fill:#333;}#mermaid-svg-wVuNwdh9RrpjtYf4 #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-wVuNwdh9RrpjtYf4 .messageText{fill:#333;stroke:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wVuNwdh9RrpjtYf4 .labelText,#mermaid-svg-wVuNwdh9RrpjtYf4 .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .loopText,#mermaid-svg-wVuNwdh9RrpjtYf4 .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-wVuNwdh9RrpjtYf4 .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-wVuNwdh9RrpjtYf4 .noteText,#mermaid-svg-wVuNwdh9RrpjtYf4 .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-wVuNwdh9RrpjtYf4 .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wVuNwdh9RrpjtYf4 .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wVuNwdh9RrpjtYf4 .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-wVuNwdh9RrpjtYf4 .actorPopupMenu{position:absolute;}#mermaid-svg-wVuNwdh9RrpjtYf4 .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-wVuNwdh9RrpjtYf4 .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-wVuNwdh9RrpjtYf4 .actor-man circle,#mermaid-svg-wVuNwdh9RrpjtYf4 line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-wVuNwdh9RrpjtYf4 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 异常发生 连接未归还 连接泄漏 获取连接创建连接执行SQL返回结果抛出异常
解决方案:
java
// 方案一:使用 try-with-resources
@Repository
public class FixedUserMapper {
@Autowired
private DataSource dataSource;
public User findById(Long id) {
// 自动关闭连接
try (Connection conn = dataSource.getConnection();
PreparedStatement ps = conn.prepareStatement("SELECT * FROM users WHERE id = ?")) {
ps.setLong(1, id);
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return mapUser(rs);
}
return null;
}
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
}
// 方案二:使用 JdbcTemplate(推荐)
@Repository
public class UserMapperImpl {
@Autowired
private JdbcTemplate jdbcTemplate;
public User findById(Long id) {
// JdbcTemplate 自动管理连接
return jdbcTemplate.queryForObject(
"SELECT * FROM users WHERE id = ?",
new Object[]{id},
this::mapUser
);
}
}
3.2 案例六:慢SQL导致数据库压力飙升
故障现象:
- 数据库CPU使用率突然飙升到100%
- 应用响应时间从50ms增加到5秒
- 监控显示大量慢查询
排查过程:
sql
-- 问题SQL:全表扫描
SELECT * FROM orders
WHERE DATE(created_at) = '2026-06-24'
AND status = 'PAID'
ORDER BY amount DESC;
-- EXPLAIN 分析
-- type: ALL (全表扫描)
-- rows: 1000000 (扫描100万行)
-- Extra: Using filesort (文件排序)
根因分析:
#mermaid-svg-ndugg2zJ6AcxOY5U{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ndugg2zJ6AcxOY5U .error-icon{fill:#552222;}#mermaid-svg-ndugg2zJ6AcxOY5U .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ndugg2zJ6AcxOY5U .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ndugg2zJ6AcxOY5U .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ndugg2zJ6AcxOY5U .marker.cross{stroke:#333333;}#mermaid-svg-ndugg2zJ6AcxOY5U svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ndugg2zJ6AcxOY5U p{margin:0;}#mermaid-svg-ndugg2zJ6AcxOY5U .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U .cluster-label text{fill:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U .cluster-label span{color:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U .cluster-label span p{background-color:transparent;}#mermaid-svg-ndugg2zJ6AcxOY5U .label text,#mermaid-svg-ndugg2zJ6AcxOY5U span{fill:#333;color:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U .node rect,#mermaid-svg-ndugg2zJ6AcxOY5U .node circle,#mermaid-svg-ndugg2zJ6AcxOY5U .node ellipse,#mermaid-svg-ndugg2zJ6AcxOY5U .node polygon,#mermaid-svg-ndugg2zJ6AcxOY5U .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-ndugg2zJ6AcxOY5U .rough-node .label text,#mermaid-svg-ndugg2zJ6AcxOY5U .node .label text,#mermaid-svg-ndugg2zJ6AcxOY5U .image-shape .label,#mermaid-svg-ndugg2zJ6AcxOY5U .icon-shape .label{text-anchor:middle;}#mermaid-svg-ndugg2zJ6AcxOY5U .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-ndugg2zJ6AcxOY5U .rough-node .label,#mermaid-svg-ndugg2zJ6AcxOY5U .node .label,#mermaid-svg-ndugg2zJ6AcxOY5U .image-shape .label,#mermaid-svg-ndugg2zJ6AcxOY5U .icon-shape .label{text-align:center;}#mermaid-svg-ndugg2zJ6AcxOY5U .node.clickable{cursor:pointer;}#mermaid-svg-ndugg2zJ6AcxOY5U .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-ndugg2zJ6AcxOY5U .arrowheadPath{fill:#333333;}#mermaid-svg-ndugg2zJ6AcxOY5U .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-ndugg2zJ6AcxOY5U .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-ndugg2zJ6AcxOY5U .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ndugg2zJ6AcxOY5U .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-ndugg2zJ6AcxOY5U .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ndugg2zJ6AcxOY5U .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-ndugg2zJ6AcxOY5U .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-ndugg2zJ6AcxOY5U .cluster text{fill:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U .cluster span{color:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-ndugg2zJ6AcxOY5U .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-ndugg2zJ6AcxOY5U rect.text{fill:none;stroke-width:0;}#mermaid-svg-ndugg2zJ6AcxOY5U .icon-shape,#mermaid-svg-ndugg2zJ6AcxOY5U .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-ndugg2zJ6AcxOY5U .icon-shape p,#mermaid-svg-ndugg2zJ6AcxOY5U .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-ndugg2zJ6AcxOY5U .icon-shape .label rect,#mermaid-svg-ndugg2zJ6AcxOY5U .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-ndugg2zJ6AcxOY5U .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-ndugg2zJ6AcxOY5U .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-ndugg2zJ6AcxOY5U :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否
SQL查询
是否使用索引?
全表扫描
扫描100万行
CPU飙升
响应超时
解决方案:
sql
-- 方案一:添加索引
ALTER TABLE orders ADD INDEX idx_created_at_status (created_at, status);
-- 方案二:优化SQL(避免对索引列使用函数)
SELECT * FROM orders
WHERE created_at >= '2026-06-24 00:00:00'
AND created_at < '2026-06-25 00:00:00'
AND status = 'PAID'
ORDER BY amount DESC;
-- 方案三:分页查询
SELECT * FROM orders
WHERE created_at >= '2026-06-24 00:00:00'
AND created_at < '2026-06-25 00:00:00'
AND status = 'PAID'
ORDER BY amount DESC
LIMIT 100;
第四章:缓存问题案例
4.1 案例七:缓存雪崩导致数据库崩溃
故障现象:
- 大量缓存同时过期
- 请求全部打到数据库
- 数据库CPU飙升,最终崩溃
排查过程:
java
// 问题代码:缓存过期时间相同
@Service
public class ProductService {
public Product getProduct(Long id) {
String cacheKey = "product:" + id;
Product product = redisTemplate.opsForValue().get(cacheKey);
if (product == null) {
product = productMapper.selectById(id);
// 问题:所有缓存设置相同的过期时间
redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
}
return product;
}
}
// 故障场景:
// 1. 10:00 大量商品缓存同时设置,过期时间都是10:30
// 2. 10:30 缓存同时过期
// 3. 10:30-10:31 大量请求直接打到数据库
// 4. 数据库崩溃
根因分析:
#mermaid-svg-9Ytq4xRZjYqT6upc{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-9Ytq4xRZjYqT6upc .error-icon{fill:#552222;}#mermaid-svg-9Ytq4xRZjYqT6upc .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-9Ytq4xRZjYqT6upc .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-9Ytq4xRZjYqT6upc .marker{fill:#333333;stroke:#333333;}#mermaid-svg-9Ytq4xRZjYqT6upc .marker.cross{stroke:#333333;}#mermaid-svg-9Ytq4xRZjYqT6upc svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-9Ytq4xRZjYqT6upc p{margin:0;}#mermaid-svg-9Ytq4xRZjYqT6upc .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc .cluster-label text{fill:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc .cluster-label span{color:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc .cluster-label span p{background-color:transparent;}#mermaid-svg-9Ytq4xRZjYqT6upc .label text,#mermaid-svg-9Ytq4xRZjYqT6upc span{fill:#333;color:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc .node rect,#mermaid-svg-9Ytq4xRZjYqT6upc .node circle,#mermaid-svg-9Ytq4xRZjYqT6upc .node ellipse,#mermaid-svg-9Ytq4xRZjYqT6upc .node polygon,#mermaid-svg-9Ytq4xRZjYqT6upc .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-9Ytq4xRZjYqT6upc .rough-node .label text,#mermaid-svg-9Ytq4xRZjYqT6upc .node .label text,#mermaid-svg-9Ytq4xRZjYqT6upc .image-shape .label,#mermaid-svg-9Ytq4xRZjYqT6upc .icon-shape .label{text-anchor:middle;}#mermaid-svg-9Ytq4xRZjYqT6upc .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-9Ytq4xRZjYqT6upc .rough-node .label,#mermaid-svg-9Ytq4xRZjYqT6upc .node .label,#mermaid-svg-9Ytq4xRZjYqT6upc .image-shape .label,#mermaid-svg-9Ytq4xRZjYqT6upc .icon-shape .label{text-align:center;}#mermaid-svg-9Ytq4xRZjYqT6upc .node.clickable{cursor:pointer;}#mermaid-svg-9Ytq4xRZjYqT6upc .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-9Ytq4xRZjYqT6upc .arrowheadPath{fill:#333333;}#mermaid-svg-9Ytq4xRZjYqT6upc .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-9Ytq4xRZjYqT6upc .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-9Ytq4xRZjYqT6upc .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9Ytq4xRZjYqT6upc .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-9Ytq4xRZjYqT6upc .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9Ytq4xRZjYqT6upc .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-9Ytq4xRZjYqT6upc .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-9Ytq4xRZjYqT6upc .cluster text{fill:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc .cluster span{color:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-9Ytq4xRZjYqT6upc .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-9Ytq4xRZjYqT6upc rect.text{fill:none;stroke-width:0;}#mermaid-svg-9Ytq4xRZjYqT6upc .icon-shape,#mermaid-svg-9Ytq4xRZjYqT6upc .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-9Ytq4xRZjYqT6upc .icon-shape p,#mermaid-svg-9Ytq4xRZjYqT6upc .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-9Ytq4xRZjYqT6upc .icon-shape .label rect,#mermaid-svg-9Ytq4xRZjYqT6upc .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-9Ytq4xRZjYqT6upc .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-9Ytq4xRZjYqT6upc .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-9Ytq4xRZjYqT6upc :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 10:00 缓存设置
10:30 缓存过期
请求打到数据库
数据库压力激增
数据库崩溃
解决方案:
java
// 方案一:随机过期时间
@Service
public class FixedProductService {
private final Random random = new Random();
public Product getProduct(Long id) {
String cacheKey = "product:" + id;
Product product = redisTemplate.opsForValue().get(cacheKey);
if (product == null) {
product = productMapper.selectById(id);
// 基础过期时间 + 随机偏移
int baseExpire = 30; // 30分钟
int randomOffset = random.nextInt(10); // 0-10分钟随机
int expire = baseExpire + randomOffset;
redisTemplate.opsForValue().set(cacheKey, product, expire, TimeUnit.MINUTES);
}
return product;
}
}
// 方案二:使用互斥锁防止缓存击穿
@Service
public class FixedProductService2 {
public Product getProduct(Long id) {
String cacheKey = "product:" + id;
String lockKey = "lock:" + cacheKey;
Product product = redisTemplate.opsForValue().get(cacheKey);
if (product == null) {
// 获取分布式锁
boolean locked = redisTemplate.opsForValue()
.setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);
if (locked) {
try {
// 双重检查
product = redisTemplate.opsForValue().get(cacheKey);
if (product == null) {
product = productMapper.selectById(id);
redisTemplate.opsForValue().set(cacheKey, product, 30, TimeUnit.MINUTES);
}
} finally {
redisTemplate.delete(lockKey);
}
} else {
// 未获取到锁,等待后重试
Thread.sleep(100);
return getProduct(id);
}
}
return product;
}
}
第五章:配置问题案例
5.1 案例八:配置错误导致服务启动失败
故障现象:
- 修改配置后服务无法启动
- 报错
Failed to bind properties under 'spring.datasource' - 回滚配置后恢复正常
排查过程:
yaml
# 问题配置:拼写错误
spring:
datasource:
drver-class-name: com.mysql.cj.jdbc.Driver # 拼写错误:drver
url: jdbc:mysql://localhost:3306/mydb
解决方案:
yaml
# 正确配置
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver # 正确:driver
url: jdbc:mysql://localhost:3306/mydb
username: root
password: ${DB_PASSWORD} # 使用环境变量,不要硬编码
5.2 案例九:环境差异导致功能异常
故障现象:
- 本地测试正常,部署到生产环境报错
- 报错
Connection refused - 生产环境的数据库地址不同
排查过程:
yaml
# 本地配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
# 生产环境应该使用不同的地址
解决方案:
yaml
# 使用多环境配置
# application.yml (公共配置)
spring:
profiles:
active: ${SPRING_PROFILES_ACTIVE:dev}
# application-dev.yml (开发环境)
spring:
datasource:
url: jdbc:mysql://localhost:3306/mydb
# application-prod.yml (生产环境)
spring:
datasource:
url: jdbc:mysql://prod-db:3306/mydb
5.3 案例十:依赖冲突导致运行时异常
故障现象:
- 服务启动成功,但运行时报错
- 报错
NoSuchMethodError - 方法签名不匹配
排查过程:
java
// 问题:两个依赖引入了不同版本的同一个类
// 依赖A: com.fasterxml.jackson:2.15.0
// 依赖B: com.fasterxml.jackson:2.12.0
// 运行时加载了错误的版本
解决方案:
xml
<!-- 排除冲突的依赖 -->
<dependency>
<groupId>com.example</groupId>
<artifactId>library-a</artifactId>
<version>1.0.0</version>
<exclusions>
<exclusion>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 统一版本 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson</groupId>
<artifactId>jackson-bom</artifactId>
<version>2.17.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
第六章:故障预防体系
6.1 监控告警配置
java
@Configuration
public class MonitorConfig {
// 内存监控
@Scheduled(fixedRate = 60000)
public void monitorMemory() {
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
MemoryUsage heapUsage = memoryBean.getHeapMemoryUsage();
long used = heapUsage.getUsed();
long max = heapUsage.getMax();
double usage = (double) used / max;
if (usage > 0.85) {
log.warn("内存使用率超过85%: {}", usage);
sendAlert("内存告警", "使用率: " + usage);
}
}
// 线程池监控
@Scheduled(fixedRate = 30000)
public void monitorThreadPool() {
ThreadPoolExecutor executor = (ThreadPoolExecutor) taskExecutor;
int activeCount = executor.getActiveCount();
int poolSize = executor.getPoolSize();
long completedTaskCount = executor.getCompletedTaskCount();
log.info("线程池状态: active={}, pool={}, completed={}",
activeCount, poolSize, completedTaskCount);
}
// 数据库连接池监控
@Scheduled(fixedRate = 30000)
public void monitorDataSource() {
HikariDataSource ds = (HikariDataSource) dataSource;
HikariPoolMXBean poolBean = ds.getHikariPoolMXBean();
int active = poolBean.getActiveConnections();
int idle = poolBean.getIdleConnections();
int total = poolBean.getTotalConnections();
log.info("连接池状态: active={}, idle={}, total={}", active, idle, total);
}
}
6.2 故障排查检查清单
markdown
【故障排查流程】
□ 1. 确认故障现象(日志、监控、用户反馈)
□ 2. 快速止损(重启、回滚、限流、降级)
□ 3. 定位问题(日志分析、监控排查、代码审查)
□ 4. 修复问题(代码修复、配置调整、数据修复)
□ 5. 验证恢复(功能测试、性能测试、监控观察)
□ 6. 复盘总结(根因分析、改进措施、文档沉淀)
【常见故障排查命令】
# 查看JVM内存
jmap -heap <pid>
# 查看线程状态
jstack <pid>
# 查看GC情况
jstat -gcutil <pid> 1s
# 查看网络连接
netstat -anp | grep <port>
# 查看数据库连接
show processlist;
总结与展望
核心要点回顾
- 内存问题:避免无限增长的集合、正确清理 ThreadLocal
- 线程问题:合理配置线程池、避免死锁
- 数据库问题:正确关闭连接、优化慢SQL
- 缓存问题:随机过期时间、使用互斥锁
- 配置问题:多环境配置、依赖版本管理
适用边界
- 必须预防:OOM、死锁、连接泄漏等致命问题
- 重点关注:缓存雪崩、慢SQL等影响性能的问题
- 持续改进:监控告警、故障演练、代码审查
未来展望
生产环境的稳定性需要持续投入。建议团队建立完善的监控体系、定期进行故障演练、培养故障排查能力,将故障消灭在萌芽状态。
真实性声明
1. 故障案例来源说明
本文收录的 10 个故障案例均来自我亲身参与或主导排查的企业级生产事故:
- 案例一、二(OOM、ThreadLocal 泄漏):来自某电商公司 2024 年 Q4 生产事故复盘记录
- 案例三、四(线程池、死锁):来自某金融科技公司 2025 年大促期间故障报告
- 案例五、六(连接池、慢 SQL):来自某 SaaS 平台 2025 年上半年运维工单
- 案例七(缓存雪崩):来自某内容平台 2025 年国庆活动故障复盘
- 案例八、九、十(配置错误、环境差异、依赖冲突):来自日常 Code Review 与上线检查记录
- 所有案例的公司名称、业务数据、具体金额均已脱敏处理,技术细节保留原貌
2. 成本核算口径说明
- 故障直接损失按电商场景平均订单金额 × 故障时长 × 转化率折损估算
- 间接损失包含品牌信誉、用户流失、运维人力成本,按直接损失的 2-3 倍估算
- 年度总损失按 10 个案例年度发生频率加权平均,非简单累加
- 预防措施 ROI = (避免的故障损失 - 投入成本) / 投入成本,未考虑贴现率
- 实际损失因业务规模、行业、SLA 要求不同会有较大差异
3. 技术版本与时效性
- 本文基于 SpringBoot 3.x、JDK 17、MySQL 8.0、Redis 7.0 生产环境整理(2026 年 6 月)
- 故障排查方法论(jmap/jstack/jstat/MAT)在 JDK 8-21 通用
- 具体配置参数(如 HikariCP、线程池参数)会随版本调整,使用时请以实际环境为准
- JVM 参数在 JDK 17+ 推荐使用
-Xlog:gc*替代-XX:+PrintGCDetails
4. 敏感信息 ${VAR} 占位符规范
本文所有涉及敏感信息的配置均使用 ${VAR} 占位符,部署时通过环境变量或 K8s Secret 注入:
| 占位符 | 用途 | 注入方式 |
|---|---|---|
${DB_PASSWORD} |
数据库密码 | K8s Secret |
${SPRING_PROFILES_ACTIVE} |
Spring 激活 profile | ConfigMap |
严禁在代码或配置文件中硬编码任何密钥、密码、API Key。
如果本文对你有帮助,欢迎点赞、收藏、转发。你在生产环境中遇到过哪些故障,欢迎在评论区分享交流。行文仓促,定有不足之处,欢迎各位朋友在评论区批评指正,不胜感激。