性能优化案例
返回性能优化案例
一、为什么要学习优化案例
只会压测还不够。
真正有价值的是:
- 能读懂结果
- 能定位原因
- 能推动优化
- 能验证优化效果
这篇文章用一个常见场景说明整个过程。
二、案例背景
假设某电商系统的“创建订单”接口在活动前做压测,结果如下:
| 指标 | 结果 |
|---|---|
| QPS | 320 |
| 平均 RT | 260ms |
| P95 | 680ms |
| 错误率 | 0.8% |
业务要求却是:
- 支撑 800 QPS
- P95 小于 300ms
- 错误率小于 0.1%
显然没有达标。
三、先看现象,不急着下结论
这时不要立刻说“系统太差”。
正确做法是先拆现象:
- QPS 上不去
- P95 很高
- 错误率偏高
继续看监控:
- 应用 CPU 65%
- JVM 没有频繁 Full GC
- 数据库连接池接近上限
- MySQL 慢 SQL 明显增加
从这里已经能判断:
大概率不是 CPU 型瓶颈,而更像数据库和线程等待问题。
四、继续定位根因
查看创建订单链路,发现主要步骤有:
- 校验用户
- 查询商品
- 校验库存
- 写订单主表
- 写订单明细表
- 发送消息
排查后发现三个问题。
1. 库存校验 SQL 没有命中合适索引
导致高并发下查询明显变慢。
2. 下单过程里有重复查询
同一商品信息在一个请求里查了两次。
3. 创建订单后同步发送消息
消息服务偶发抖动时,会拖慢整个主流程。
五、第一轮优化
先做改动成本较低的优化:
1. 给库存表补索引
目标:
- 减少库存查询耗时
2. 去掉重复查询
目标:
- 降低数据库访问次数
3. 消息发送改为异步
目标:
- 缩短主流程耗时
优化后重新压测,结果如下:
| 指标 | 优化前 | 优化后 |
|---|---|---|
| QPS | 320 | 560 |
| 平均 RT | 260ms | 180ms |
| P95 | 680ms | 420ms |
| 错误率 | 0.8% | 0.2% |
明显变好了,但还没有完全达标。
六、第二轮优化
继续看监控,发现:
- Redis 命中率较低
- 商品信息查询仍频繁回源数据库
于是第二轮优化做了:
1. 商品信息增加热点缓存
把高频读取的数据优先从 Redis 返回。
2. 调整数据库连接池参数
避免高并发下连接等待过长。
3. 调整订单服务线程池
减少请求积压。
再次压测,结果如下:
| 指标 | 第二轮结果 |
|---|---|
| QPS | 860 |
| 平均 RT | 130ms |
| P95 | 260ms |
| 错误率 | 0.06% |
这时已经基本达标。
七、这个案例的关键经验
这个优化案例有几个很重要的经验。
1. 不要只盯着代码本身
性能问题很可能来自:
- 索引
- 缓存
- 线程池
- 消息调用
2. 优化要分轮次做
一次改太多,会有两个问题:
- 不知道到底哪项优化起作用
- 风险更难控制
3. 每次优化都要重新压测
没有复测,就不能证明优化真的有效。
八、适合 Java 后端工程师的优化顺序
如果你在工作中遇到类似问题,可以优先按这个顺序排查:
- SQL 和索引
- 缓存命中率
- 线程池和连接池
- 同步调用是否过多
- JVM 和 GC
这不是绝对顺序,但对大多数业务系统都比较实用。
九、优化时常见误区
1. 还没定位就先改 JVM 参数
这很常见,但往往不是第一优先级。
2. 只优化平均响应时间
真正影响体验的通常是长尾,也就是 P95 / P99。
3. 优化后不做对比记录
没有对比表,后面很难复盘。
4. 忽略业务正确性
有些“优化”是通过省略校验换来的,这种不能要。
十、总结
性能优化案例最重要的不是记住某一个技巧,而是掌握完整方法:
- 先看现象
- 再结合监控定位
- 做低风险、高收益的优化
- 复测验证
- 继续迭代
对 Java 后端工程师来说,这套方法比单纯记几个调优参数更有价值。