SpringNotes

第 25 章:性能调优

zjc 于 2026-01-25 发布

这是《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 错误

资源:

  1. CPU;
  2. 内存;
  3. 线程池;
  4. 连接池;
  5. 数据库;
  6. Redis;
  7. 网络;
  8. 磁盘 IO;
  9. 下游服务。

25.3 压测

准备:

  1. 生产相似数据量;
  2. 相似流量分布;
  3. 相同配置;
  4. 预热时间;
  5. 固定随机种子;
  6. 监控完整。

工具:

工具 适用
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 缓存优化

检查:

  1. 命中率;
  2. 回源量;
  3. 热 key;
  4. 大 key;
  5. 序列化成本;
  6. TTL 策略。

热 key:

  1. 本地缓存;
  2. key 打散;
  3. 请求合并;
  4. 限流;
  5. 预热。

大 key:

  1. 拆分集合;
  2. 分页读取;
  3. 压缩;
  4. 避免整包 JSON 无界增长;
  5. 设置容量上限。

25.7 线程与异步

线程池监控:

active
pool size
queue size
rejected
task latency

虚拟线程适合大量阻塞 IO 任务,但要关注:

  1. pinning;
  2. ThreadLocal 使用;
  3. 下游容量;
  4. 调度监控;
  5. 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

不要只调大线程。若下游慢,线程越多只会放大故障。应同时:

  1. 设置下游超时;
  2. 隔离依赖;
  3. 限流;
  4. 减少阻塞;
  5. 扩容核心瓶颈。

25.10 容量模型

估算:

目标 QPS = 峰值均值 * 峰值系数 * 增长系数
实例数 = 目标 QPS / 单实例安全 QPS + 冗余

冗余要满足:

  1. 一个实例故障仍承载流量;
  2. 发布期间可用容量不下降;
  3. 突发流量有余量;
  4. HPA 冷启动时间;
  5. 数据库和中间件同步扩容。

本章小结

性能调优应从业务指标和资源饱和度入手,用压测建立基线,用 trace 和 profile 定位瓶颈。常见瓶颈通常在数据库、下游调用、缓存、线程池和序列化,而不是 JVM 参数。每次优化都要量化收益、验证副作用并更新容量模型。

思考题

  1. 为什么 P99 比平均值更重要?
  2. 如何判断连接池是否是瓶颈?
  3. 什么情况下增加 Tomcat 线程无效?
  4. 虚拟线程解决什么问题?
  5. 如何设计容量冗余?