性能调优综合方法论
问题
系统整体性能不达标,如何从 JVM、数据库、缓存、网络、代码多个维度系统性调优?
答案
调优原则
- 先度量后优化:没有数据就没有优化
- 优化瓶颈点:用 Amdahl 定律,优化占比最大的部分
- 空间换时间:缓存、预计算、冗余
- 避免过早优化:先保证正确性
调优全景图
JVM 调优
JVM 推荐配置(8C16G)
# 堆配置
-Xms8g -Xmx8g # 堆大小固定,避免动态扩缩
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m
# GC 选择
# JDK 11+: G1(默认)
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200 # 目标停顿 200ms
-XX:G1HeapRegionSize=8m
-XX:InitiatingHeapOccupancyPercent=45
# JDK 17+: ZGC(超低延迟)
-XX:+UseZGC
-XX:+ZGenerational # JDK 21 分代 ZGC
数据库调优
| 维度 | 优化点 |
|---|---|
| SQL | EXPLAIN 分析、避免全表扫描、减少 JOIN |
| 索引 | 覆盖索引、联合索引最左前缀、避免索引失效 |
| 连接池 | HikariCP,10-20 连接 |
| 读写分离 | 查询走从库 |
| 分库分表 | 单表超 500 万考虑 |
缓存调优
多级缓存提升性能
// L1: 本地缓存(Caffeine,ms 级)
// L2: Redis 缓存(ms 级)
// L3: 数据库
public Object getData(String key) {
// L1
Object value = localCache.getIfPresent(key);
if (value != null) return value;
// L2
value = redisTemplate.opsForValue().get(key);
if (value != null) {
localCache.put(key, value);
return value;
}
// L3
value = db.query(key);
redisTemplate.opsForValue().set(key, value, 30, TimeUnit.MINUTES);
localCache.put(key, value);
return value;
}
关注指标:
- 缓存命中率 > 95%
- Redis RT < 5ms
代码层调优
| 优化 | 示例 |
|---|---|
| 批量操作 | 批量 INSERT 代替循环单条 INSERT |
| 异步化 | 非核心逻辑用 MQ / @Async |
| 并行化 | CompletableFuture 并行调用多个服务 |
| 减少序列化 | Protobuf / Kryo 代替 JSON |
| 对象复用 | 连接池、线程池、对象池 |
CompletableFuture 并行调用
// 串行:300ms + 200ms + 100ms = 600ms
// 并行:max(300, 200, 100) = 300ms
CompletableFuture<User> userFuture = CompletableFuture.supplyAsync(() -> userService.getUser(id));
CompletableFuture<List<Order>> orderFuture = CompletableFuture.supplyAsync(() -> orderService.list(id));
CompletableFuture<Integer> pointFuture = CompletableFuture.supplyAsync(() -> pointService.getPoints(id));
CompletableFuture.allOf(userFuture, orderFuture, pointFuture).join();
UserProfileVO vo = new UserProfileVO();
vo.setUser(userFuture.get());
vo.setOrders(orderFuture.get());
vo.setPoints(pointFuture.get());
常见面试问题
Q1: 你做过哪些性能优化?
答案:(按场景回答)
用 STAR 法则:
- S(场景):大促期间接口 P99 从 500ms 飙升到 3s
- T(任务):优化到 P99 < 200ms
- A(行动):链路追踪定位 → 慢 SQL 加索引 → 热点数据加缓存 → 非核心链路异步化
- R(结果):P99 降到 100ms,QPS 提升 3 倍
Q2: 接口从 1s 优化到 100ms,思路是什么?
答案:
- 链路追踪找慢点
- 数据库层:加索引、缓存热点查询
- 服务层:并行调用、异步化非核心逻辑
- 网络层:连接复用、减少序列化开销
- 代码层:批量操作、减少不必要的计算
Q3: GC 导致的延迟怎么优化?
答案:
- 升级 GC:CMS → G1 → ZGC
- 调大堆减少 GC 频率
- 减少对象分配(对象池复用)
- 预热:启动后先跑一些请求让 JIT 编译
详见 频繁 Full GC、JVM 调优。