性能优化案例

返回

性能优化案例

一、为什么要学习优化案例

只会压测还不够。

真正有价值的是:

  • 能读懂结果
  • 能定位原因
  • 能推动优化
  • 能验证优化效果

这篇文章用一个常见场景说明整个过程。


二、案例背景

假设某电商系统的“创建订单”接口在活动前做压测,结果如下:

指标结果
QPS320
平均 RT260ms
P95680ms
错误率0.8%

业务要求却是:

  • 支撑 800 QPS
  • P95 小于 300ms
  • 错误率小于 0.1%

显然没有达标。


三、先看现象,不急着下结论

这时不要立刻说“系统太差”。

正确做法是先拆现象:

  • QPS 上不去
  • P95 很高
  • 错误率偏高

继续看监控:

  • 应用 CPU 65%
  • JVM 没有频繁 Full GC
  • 数据库连接池接近上限
  • MySQL 慢 SQL 明显增加

从这里已经能判断:

大概率不是 CPU 型瓶颈,而更像数据库和线程等待问题。


四、继续定位根因

查看创建订单链路,发现主要步骤有:

  1. 校验用户
  2. 查询商品
  3. 校验库存
  4. 写订单主表
  5. 写订单明细表
  6. 发送消息

排查后发现三个问题。

1. 库存校验 SQL 没有命中合适索引

导致高并发下查询明显变慢。

2. 下单过程里有重复查询

同一商品信息在一个请求里查了两次。

3. 创建订单后同步发送消息

消息服务偶发抖动时,会拖慢整个主流程。


五、第一轮优化

先做改动成本较低的优化:

1. 给库存表补索引

目标:

  • 减少库存查询耗时

2. 去掉重复查询

目标:

  • 降低数据库访问次数

3. 消息发送改为异步

目标:

  • 缩短主流程耗时

优化后重新压测,结果如下:

指标优化前优化后
QPS320560
平均 RT260ms180ms
P95680ms420ms
错误率0.8%0.2%

明显变好了,但还没有完全达标。


六、第二轮优化

继续看监控,发现:

  • Redis 命中率较低
  • 商品信息查询仍频繁回源数据库

于是第二轮优化做了:

1. 商品信息增加热点缓存

把高频读取的数据优先从 Redis 返回。

2. 调整数据库连接池参数

避免高并发下连接等待过长。

3. 调整订单服务线程池

减少请求积压。

再次压测,结果如下:

指标第二轮结果
QPS860
平均 RT130ms
P95260ms
错误率0.06%

这时已经基本达标。


七、这个案例的关键经验

这个优化案例有几个很重要的经验。

1. 不要只盯着代码本身

性能问题很可能来自:

  • 索引
  • 缓存
  • 线程池
  • 消息调用

2. 优化要分轮次做

一次改太多,会有两个问题:

  • 不知道到底哪项优化起作用
  • 风险更难控制

3. 每次优化都要重新压测

没有复测,就不能证明优化真的有效。


八、适合 Java 后端工程师的优化顺序

如果你在工作中遇到类似问题,可以优先按这个顺序排查:

  1. SQL 和索引
  2. 缓存命中率
  3. 线程池和连接池
  4. 同步调用是否过多
  5. JVM 和 GC

这不是绝对顺序,但对大多数业务系统都比较实用。


九、优化时常见误区

1. 还没定位就先改 JVM 参数

这很常见,但往往不是第一优先级。

2. 只优化平均响应时间

真正影响体验的通常是长尾,也就是 P95 / P99。

3. 优化后不做对比记录

没有对比表,后面很难复盘。

4. 忽略业务正确性

有些“优化”是通过省略校验换来的,这种不能要。


十、总结

性能优化案例最重要的不是记住某一个技巧,而是掌握完整方法:

  1. 先看现象
  2. 再结合监控定位
  3. 做低风险、高收益的优化
  4. 复测验证
  5. 继续迭代

对 Java 后端工程师来说,这套方法比单纯记几个调优参数更有价值。