一、性能测试到底在测什么?
说到性能测试,很多同学第一反应就是"跑个压测看看QPS"。其实性能测试是个系统工程,就像去医院体检不能只看心跳一样。完整的性能测试应该包含以下几个维度:
- 基准测试:系统在理想状态下的表现
- 负载测试:逐步增加压力观察变化
- 压力测试:突破极限找到瓶颈点
- 稳定性测试:长时间运行看内存泄漏
- 异常测试:模拟网络抖动等异常场景
举个实际案例,我们去年优化过一个电商秒杀系统。最初只做了简单的JMeter压测,结果上线后数据库连接池直接爆了。后来我们补充了混合场景测试(下单+查询+支付),才发现连接池配置根本不够用。
二、如何读懂性能测试报告?
拿到一份性能测试报告,别被那些花花绿绿的图表吓到。关键要看这几个核心指标:
- 响应时间:重点关注95线和99线
- 吞吐量:系统处理能力的天花板
- 错误率:超过1%就要亮红灯
- 资源使用率:CPU、内存、IO的消耗曲线
这里我用JMeter测试一个SpringBoot接口的示例报告片段:
Sample Count: 10000
Average: 235ms
Min: 98ms
Max: 1892ms
90% Line: 312ms
95% Line: 498ms
99% Line: 1023ms
Error%: 0.12%
Throughput: 328.7/sec
这个报告告诉我们:
- 平均响应235ms还不错
- 但99线用户要等1秒以上(需要优化)
- 错误率在可控范围
- 吞吐量328QPS是当前极限
三、常见性能问题定位技巧
3.1 CPU飙高怎么查?
先用top找到最吃CPU的进程,然后用arthas的thread命令查看线程堆栈:
// 安装arthas
curl -O https://arthas.aliyun.com/arthas-boot.jar
java -jar arthas-boot.jar
// 查看线程CPU占用
thread -n 3
// 输出示例:
Thread Id Name State CPU%
46 http-nio-8080-exec-5 RUNNABLE 78%
34 http-nio-8080-exec-3 RUNNABLE 65%
发现是Tomcat线程吃满CPU,大概率是代码里有死循环或者复杂运算。
3.2 内存泄漏怎么抓?
用jmap+jhat组合拳:
# 生成堆dump
jmap -dump:format=b,file=heap.hprof <pid>
# 分析堆dump
jhat heap.hprof
然后在浏览器访问http://localhost:7000,查看对象引用链。常见的内存泄漏包括:
- 静态集合不断添加元素
- 未关闭的数据库连接
- 缓存没有过期策略
四、系统优化实战指南
4.1 数据库优化三板斧
- 加索引:比如给用户表的手机号字段加索引
ALTER TABLE users ADD INDEX idx_mobile(mobile);
- SQL优化:避免全表扫描
-- 反例:使用左模糊查询
SELECT * FROM orders WHERE order_no LIKE '%123%';
-- 正例:使用右模糊查询
SELECT * FROM orders WHERE order_no LIKE '123%';
- 分库分表:当单表超过500万行考虑拆分
// 使用ShardingJDBC配置分表
spring.shardingsphere.datasource.names=ds0
spring.shardingsphere.sharding.tables.orders.actual-data-nodes=ds0.orders_$->{0..1}
spring.shardingsphere.sharding.tables.orders.table-strategy.inline.sharding-column=user_id
spring.shardingsphere.sharding.tables.orders.table-strategy.inline.algorithm-expression=orders_$->{user_id % 2}
4.2 缓存使用的正确姿势
缓存虽好,但用错了反而会拖慢系统。记住这几个原则:
- 缓存热点数据,不是所有数据
- 设置合理的过期时间
- 考虑缓存穿透问题
用Redis的示例:
// 伪代码:查询用户信息
public User getUser(Long id) {
// 1. 先查缓存
String key = "user:" + id;
User user = redis.get(key);
if (user != null) {
return user;
}
// 2. 查数据库
user = userDao.findById(id);
if (user == null) {
// 防止缓存穿透,设置空值
redis.setex(key, 300, "null");
return null;
}
// 3. 写入缓存
redis.setex(key, 3600, user);
return user;
}
4.3 异步化改造
把同步操作改成异步,能显著提升吞吐量。比如用Spring的@Async:
@Service
public class OrderService {
@Async // 声明为异步方法
public void asyncProcessOrder(Order order) {
// 耗时操作
generateInvoice(order);
sendEmailNotification(order);
}
}
记得要在启动类加@EnableAsync注解。异步化要注意:
- 线程池要合理配置
- 要考虑消息丢失的情况
- 错误处理要完善
五、避坑指南与最佳实践
5.1 测试环境要与生产一致
很多团队在测试环境优化得很好,上线就崩了。主要是因为:
- 测试环境硬件配置缩水
- 测试数据量不够
- 网络环境不同
建议至少保证:
- 数据库数据量相当
- 服务器配置同规格
- 网络拓扑一致
5.2 不要过早优化
记住Knuth大神的名言:"过早优化是万恶之源"。优化前要先:
- 确认是否真的存在性能问题
- 找到真正的瓶颈点
- 评估优化投入产出比
5.3 监控比优化更重要
没有监控的优化就像蒙眼开车。推荐监控三板斧:
- 指标采集:Prometheus
- 日志收集:ELK
- 链路追踪:SkyWalking
六、总结与展望
性能优化是个持续的过程,没有一劳永逸的银弹。我的建议是:
- 建立性能基线
- 定期进行性能测试
- 每次变更都评估性能影响
未来性能优化的趋势会是:
- 基于AI的自动调参
- 云原生架构下的可观测性
- 硬件加速(如DPU)的应用
记住,优化的终极目标不是追求数字好看,而是为用户提供流畅的体验。有时候把99线从1000ms降到800ms,比把平均响应从200ms降到150ms更有价值。
评论