这是《Spring Boot 与 Spring Cloud 零基础实战指南》的独立章节版。本章从概念、实操和生产排查三个视角展开,代码块保留了原书可直接运行的版本。 性能调优要先定义目标、建立基线、找到瓶颈,再做最小改动并验证。盲目改配置容易把系统调得更复杂,却没有改善用户指标。
25.1 性能指标
| 指标 | 说明 |
|---|---|
| QPS | 每秒请求量 |
| RT / P50 / P95 / P99 | 响应时间分布 |
| error rate | 错误率 |
| saturation | 饱和度 |
| throughput | 吞吐 |
| cost per request | 单请求成本 |
| concurrency | 并发 |
目标示例:
在 4C8G、P99 <= 200ms、错误率 < 0.1% 的条件下,
单实例支撑 1000 QPS。
只说“更快”没有工程意义。
25.2 USE 方法
对每个资源看:
Utilization 使用率
Saturation 饱和度
Errors 错误
资源:
- CPU;
- 内存;
- 线程池;
- 连接池;
- 数据库;
- Redis;
- 网络;
- 磁盘 IO;
- 下游服务。
25.3 压测
准备:
- 生产相似数据量;
- 相似流量分布;
- 相同配置;
- 预热时间;
- 固定随机种子;
- 监控完整。
工具:
| 工具 | 适用 |
|---|---|
| wrk / wrk2 | HTTP 高并发 |
| JMeter | 复杂场景 |
| Gatling | 代码化场景 |
| k6 | 云原生压测 |
报告:
场景
数据规模
并发梯度
QPS
P50 / P95 / P99 / P999
错误率
CPU / Memory / GC
DB / Redis / downstream 指标
结论
25.4 JVM 调优
常见检查:
1. GC 停顿分布
2. Live 数据
3. 分配速率
4. 安全点等待
5. 容器 CPU limit
6. 线程数
配置:
java -Xms4g -Xmx4g \
-XX:+UseG1GC \
-XX:MaxMetaspaceSize=512m \
-Xlog:gc*,safepoint:file=/logs/gc.log:time,uptime,level,tags \
-jar app.jar
如果 GC 总时间占比低,继续调 JVM 通常收益有限。
25.5 数据库优化
慢 SQL 处理:
1. 确认扫描行数
2. 查看 explain
3. 检查索引
4. 减少返回字段
5. 拆深分页
6. 避免函数导致索引失效
7. 控制事务时长
深分页优化:
select id, user_id, amount
from orders
where id > 100000
order by id
limit 20;
连接池:
active == max 且 pending > 0
可能是连接池不足,也可能是 SQL 太慢。先看慢 SQL 和持连接时间。
25.6 缓存优化
检查:
- 命中率;
- 回源量;
- 热 key;
- 大 key;
- 序列化成本;
- TTL 策略。
热 key:
- 本地缓存;
- key 打散;
- 请求合并;
- 限流;
- 预热。
大 key:
- 拆分集合;
- 分页读取;
- 压缩;
- 避免整包 JSON 无界增长;
- 设置容量上限。
25.7 线程与异步
线程池监控:
active
pool size
queue size
rejected
task latency
虚拟线程适合大量阻塞 IO 任务,但要关注:
- pinning;
- ThreadLocal 使用;
- 下游容量;
- 调度监控;
- synchronized 长临界区。
异步不是提升总量,只是改变等待方式。下游容量不变时,无限异步只会把压力后移。
25.8 接口优化
常见手段:
| 问题 | 优化 |
|---|---|
| N+1 查询 | 批量查询 |
| 大对象序列化 | DTO 精简、压缩 |
| 重复查询 | 缓存 |
| 串行无依赖调用 | 并行化 |
| 每次创建重对象 | 复用 |
| 同步通知 | 异步化 |
| 全量加载 | 分页 |
| 复杂计算 | 预计算 |
并行调用示例:
CompletableFuture<ProductView> productFuture =
CompletableFuture.supplyAsync(() -> productClient.get(id), executor);
CompletableFuture<InventoryView> inventoryFuture =
CompletableFuture.supplyAsync(() -> inventoryClient.get(id), executor);
CompletableFuture.allOf(productFuture, inventoryFuture).join();
必须设置超时和异常处理。
25.9 Web 容器调优
Tomcat 配置:
server:
tomcat:
threads:
max: 200
min-spare: 20
max-connections: 8192
accept-count: 100
connection-timeout: 5s
不要只调大线程。若下游慢,线程越多只会放大故障。应同时:
- 设置下游超时;
- 隔离依赖;
- 限流;
- 减少阻塞;
- 扩容核心瓶颈。
25.10 容量模型
估算:
目标 QPS = 峰值均值 * 峰值系数 * 增长系数
实例数 = 目标 QPS / 单实例安全 QPS + 冗余
冗余要满足:
- 一个实例故障仍承载流量;
- 发布期间可用容量不下降;
- 突发流量有余量;
- HPA 冷启动时间;
- 数据库和中间件同步扩容。
本章小结
性能调优应从业务指标和资源饱和度入手,用压测建立基线,用 trace 和 profile 定位瓶颈。常见瓶颈通常在数据库、下游调用、缓存、线程池和序列化,而不是 JVM 参数。每次优化都要量化收益、验证副作用并更新容量模型。
思考题
- 为什么 P99 比平均值更重要?
- 如何判断连接池是否是瓶颈?
- 什么情况下增加 Tomcat 线程无效?
- 虚拟线程解决什么问题?
- 如何设计容量冗余?