性能调优实战
性能调优是系统优化的核心环节,包括 JVM 调优、SQL 优化、系统调优等多个层面。本文详解性能调优的方法论、工具和实战技巧。
一、性能调优方法论
1.1 调优流程
graph TB
A[1. 性能测试<br/>基准测试/负载测试/压力测试] --> B[2. 性能分析<br/>瓶颈定位/profiling/日志分析]
B --> C[3. 优化实施<br/>代码优化/参数调优/架构优化]
C --> D[4. 效果验证<br/>对比测试/监控观察/回归测试]
D --> E[5. 持续优化<br/>定期性能测试/监控告警/容量规划]
1.2 性能指标
| 指标 | 说明 | 目标值 |
|---|---|---|
| QPS | 每秒查询数 | 根据业务定 |
| TPS | 每秒事务数 | 根据业务定 |
| 响应时间 | P50/P95/P99 | P99 < 500ms |
| 错误率 | 失败请求占比 | < 0.1% |
| CPU 使用率 | CPU 占用 | < 70% |
| 内存使用率 | 堆内存占用 | < 80% |
| GC 时间 | GC 暂停时间 | < 100ms |
二、JVM 调优
2.1 JVM 内存结构
graph TB
subgraph Heap[Heap 堆]
direction LR
subgraph Young[Young Gen]
E[Eden]
S1[Survivor 0]
S2[Survivor 1]
end
O[Old Gen]
end
subgraph NonHeap[Non-Heap 非堆]
M[Metaspace 元空间]
C[Code Cache 代码缓存]
S[Stack 栈]
end
Heap --> NonHeap
2.2 GC 算法选择
mindmap
root((GC 算法选择))
Serial
吞吐量:低
延迟:高
适用:单线程
Parallel
吞吐量:高
延迟:中
适用:批处理
CMS
吞吐量:中
延迟:低
适用:Web 应用
G1
吞吐量:高
延迟:低
适用:大堆<32GB
ZGC
吞吐量:高
延迟:极低
适用:超大堆>32GB
2.3 JVM 参数配置
# 生产环境推荐配置(8GB 堆)
java -Xms8g -Xmx8g \ # 堆大小(最小=最大)
-XX:+UseG1GC \ # 使用 G1 GC
-XX:MaxGCPauseMillis=200 \ # 最大 GC 暂停时间
-XX:G1HeapRegionSize=16m \ # G1 区域大小
-XX:InitiatingHeapOccupancyPercent=45 \ # 并发 GC 触发阈值
-XX:+ParallelRefProcEnabled \ # 并行引用处理
-XX:+UseStringDeduplication \ # 字符串去重
-XX:MaxMetaspaceSize=512m \ # 元空间大小
-XX:ReservedCodeCacheSize=240m \ # 代码缓存大小
-XX:+HeapDumpOnOutOfMemoryError \ # OOM 时 dump
-XX:HeapDumpPath=/logs/ \ # dump 文件路径
-Xlog:gc*:file=/logs/gc.log:time,uptime:filecount=5,filesize=100m \
-jar app.jar
# 大堆配置(32GB+,使用 ZGC)
java -Xms32g -Xmx32g \
-XX:+UseZGC \
-XX:ZCollectionInterval=5 \
-XX:ZAllocationSpikeTolerance=2 \
-jar app.jar
2.4 GC 日志分析
# GC 日志示例
[GC (G1 Evacuation Pause) 2048M->512M(8192M), 0.0523456 secs]
[GC (G1 Concurrency Start) 2048M->2048M(8192M), 0.0001234 secs]
[GC (G1 Evacuation Pause) 3072M->768M(8192M), 0.0456789 secs]
# 分析工具
# 1. GCEasy(在线)
https://gceasy.io/
# 2. GCViewer(本地)
java -jar gcviewer.jar gc.log
# 3. 命令行分析
jstat -gcutil <pid> 1000 10
# 输出解读
S0 S1 E O M CCS YGC YGCT FGC FGCT GCT
0.00 0.00 50.00 40.00 80.00 10.00 100 5.000 2 0.500 5.500
S0/S1: Survivor 区使用率
E: Eden 区使用率
O: Old 区使用率
M: Metaspace 使用率
YGC/FGC: Young/Old GC 次数
YGCT/FGCT: Young/Old GC 总耗时
2.5 内存泄漏排查
# 1. 查看堆内存
jmap -heap <pid>
# 2. dump 堆内存
jmap -dump:format=b,file=heap.hprof <pid>
# 3. 分析堆 dump(MAT)
# 下载 Eclipse Memory Analyzer
# 打开 heap.hprof 文件
# 4. 查找泄漏嫌疑
# - Dominator Tree(支配树)
# - Histogram(直方图)
# - OOM Leaks(泄漏检测)
# 5. 常见泄漏原因
# - 静态集合类(static Map/List)
# - 未关闭的资源(Connection、Stream)
# - ThreadLocal 未清理
# - 监听器未注销
三、SQL 优化
3.1 执行计划分析
-- 查看执行计划
EXPLAIN SELECT * FROM orders WHERE user_id = 123 AND status = 'UNPAID';
-- 输出解读
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------+
| 1 | SIMPLE | orders | NULL | index | idx_user | idx_user| 8 | const| 100 | 10.00 | Using |
+----+-------------+--------+------------+-------+---------------+---------+---------+------+------+----------+-------+
type 类型(性能从好到差):
├── system:系统表(最优)
├── const:主键/唯一索引
├── eq_ref:主键/唯一索引连接
├── ref:非唯一索引
├── range:索引范围扫描
├── index:全索引扫描
└── ALL:全表扫描(最差)
Extra 字段:
├── Using index:覆盖索引(好)
├── Using where:WHERE 过滤
├── Using temporary:临时表(需优化)
└── Using filesort:文件排序(需优化)
3.2 索引优化
-- 1. 创建复合索引(最左前缀原则)
CREATE INDEX idx_user_status ON orders(user_id, status, create_time);
-- 有效查询
SELECT * FROM orders WHERE user_id = 123; -- ✅
SELECT * FROM orders WHERE user_id = 123 AND status = 'A'; -- ✅
SELECT * FROM orders WHERE user_id = 123 AND status = 'A' AND create_time > '2024-01-01'; -- ✅
-- 无效查询
SELECT * FROM orders WHERE status = 'A'; -- ❌
SELECT * FROM orders WHERE status = 'A' AND create_time > '2024-01-01'; -- ❌
-- 2. 覆盖索引(避免回表)
SELECT user_id, status FROM orders WHERE user_id = 123; -- ✅ 覆盖索引
-- 3. 索引下推(5.6+)
SELECT * FROM orders WHERE user_id = 123 AND name LIKE '张%';
-- 索引下推:先在索引中过滤 name,再回表
-- 4. 避免索引失效
SELECT * FROM orders WHERE DATE(create_time) = '2024-01-01'; -- ❌ 函数导致失效
SELECT * FROM orders WHERE create_time >= '2024-01-01 00:00:00' AND create_time < '2024-01-02 00:00:00'; -- ✅
SELECT * FROM orders WHERE user_id != 123; -- ❌ 负向查询
SELECT * FROM orders WHERE user_id = 123 OR user_id = 456; -- ✅ UNION ALL
SELECT * FROM orders WHERE name LIKE '%张%'; -- ❌ 前缀通配符
SELECT * FROM orders WHERE name LIKE '张%'; -- ✅
3.3 SQL 优化案例
-- 案例 1:分页优化
-- 优化前(深分页慢)
SELECT * FROM orders ORDER BY create_time DESC LIMIT 100000, 20;
-- 优化后(子查询)
SELECT o.* FROM orders o
INNER JOIN (
SELECT id FROM orders ORDER BY create_time DESC LIMIT 100000, 20
) tmp ON o.id = tmp.id;
-- 优化后(延迟关联)
SELECT * FROM orders
WHERE create_time <= (SELECT create_time FROM orders ORDER BY create_time DESC LIMIT 100000, 1)
ORDER BY create_time DESC LIMIT 20;
-- 案例 2:JOIN 优化
-- 优化前(大表 JOIN)
SELECT o.*, u.name FROM orders o
LEFT JOIN user u ON o.user_id = u.id
WHERE o.create_time > '2024-01-01';
-- 优化后(小表驱动大表)
SELECT o.*, u.name FROM user u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.create_time > '2024-01-01';
-- 案例 3:UNION 优化
-- 优化前
SELECT * FROM orders WHERE user_id = 123
UNION
SELECT * FROM orders WHERE status = 'UNPAID';
-- 优化后
SELECT * FROM orders WHERE user_id = 123
UNION ALL -- 不去重
SELECT * FROM orders WHERE status = 'UNPAID';
3.4 慢查询优化
-- 1. 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 1; -- 超过 1 秒记录
SET GLOBAL log_queries_not_using_indexes = 'ON';
-- 2. 查看慢查询
SHOW VARIABLES LIKE 'slow_query_log_file';
-- 3. 分析慢查询
mysqldumpslow -s t -t 10 /var/log/mysql/slow.log
-- 4. 使用 pt-query-digest
pt-query-digest /var/log/mysql/slow.log > slow_report.txt
-- 5. 慢查询报告
# 排名靠前的 SQL
# 执行次数、总时间、平均时间
# 建议添加索引
四、系统调优
4.1 Linux 内核参数
# /etc/sysctl.conf 配置
# 1. 文件句柄数
fs.file-max = 2097152
fs.nr_open = 2097152
# 2. TCP 参数
net.core.somaxconn = 65535
net.ipv4.tcp_max_syn_backlog = 65535
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.ip_local_port_range = 1024 65535
# 3. 内存参数
vm.swappiness = 10 # 减少 swap 使用
vm.overcommit_memory = 1 # 允许过度分配
vm.dirty_ratio = 40
vm.dirty_background_ratio = 10
# 应用配置
sysctl -p
# 4. 用户限制(/etc/security/limits.conf)
* soft nofile 65535
* hard nofile 65535
* soft nproc 65535
* hard nproc 65535
4.2 性能监控工具
# 1. CPU 监控
top # 实时进程监控
htop # 增强版 top
mpstat -P ALL 1 # 每核 CPU 使用率
pidstat -u 1 # 进程 CPU 使用率
# 2. 内存监控
free -h # 内存使用
vmstat 1 # 虚拟内存统计
pidstat -r 1 # 进程内存使用
# 3. 磁盘 IO
iostat -x 1 # IO 统计
iotop # IO 进程监控
pidstat -d 1 # 进程 IO 使用
# 4. 网络监控
ss -tan # TCP 连接
netstat -tan # 网络连接
iftop # 网络流量监控
tcpdump -i eth0 port 80 # 抓包分析
# 5. 综合监控
sar -u -r -d -n 1 # 系统活动报告
dstat # 全能监控工具
4.3 性能分析工具
# 1. Java Profiling
jstack <pid> # 线程 dump
jmap <pid> # 堆 dump
jstat <pid> # GC 统计
# 2. Arthas(阿里开源)
java -jar arthas-boot.jar
# 常用命令:
thread -n 3 # 最忙线程
dashboard # 仪表盘
trace # 方法调用追踪
profiler # CPU 分析
# 3. Async Profiler
./profiler.sh -d 30 -f profile.html <pid>
# 生成火焰图
# 4. Perf(Linux)
perf top # 实时性能分析
perf record -g # 记录调用栈
perf report # 分析报告
# 5. eBPF 工具
bpftrace -l 'tracepoint:*' # 可用探针
bpftrace -e 'tracepoint:syscalls:sys_enter_open /pid == 123/ { printf("%s\n", comm); }'
五、性能测试
5.1 JMeter 压测
JMeter 压测配置
线程组配置:
├── 线程数:100-1000(根据场景)
├── Ramp-Up 时间:10-60 秒
└── 循环次数:永远/指定次数
HTTP 请求:
├── 服务器名称:order-service
├── 端口:8080
├── 方法:POST
└── 路径:/api/orders
监听器:
├── 查看结果树(调试用)
├── 聚合报告(核心指标)
├── 响应时间图
└── 后端监听器(InfluxDB + Grafana)
命令行执行:
jmeter -n -t test_plan.jmx -l result.jtl -e -o report_folder
5.2 压测报告
压测报告模板
测试场景:订单创建接口
测试时间:2024-01-01 10:00-11:00
并发用户:100
测试时长:30 分钟
测试结果:
├── 总请求数:1,800,000
├── QPS:1000
├── 平均响应时间:85ms
├── P95 响应时间:150ms
├── P99 响应时间:250ms
├── 错误率:0.05%
└── 吞吐量:50MB/s
资源使用:
├── CPU:65%
├── 内存:70%
├── 磁盘 IO:40%
└── 网络:30%
瓶颈分析:
├── 数据库慢查询:3 个 SQL 超过 500ms
├── GC 暂停:平均 50ms,最大 200ms
└── 线程池:活跃线程接近上限
优化建议:
├── 添加数据库索引
├── 调整 JVM 参数
└── 扩容线程池
六、总结
6.1 调优检查清单
性能调优检查清单
JVM 调优:
☐ 堆大小合理设置
☐ GC 算法选择合适
☐ GC 参数优化
☐ 无内存泄漏
SQL 优化:
☐ 慢查询分析
☐ 索引优化
☐ 执行计划分析
☐ 避免全表扫描
系统调优:
☐ 文件句柄数
☐ TCP 参数
☐ 内存参数
☐ 磁盘 IO
性能测试:
☐ 基准测试
☐ 负载测试
☐ 压力测试
☐ 稳定性测试
6.2 核心原则
- 数据驱动:基于监控数据,不盲目调优
- 逐步优化:一次只改一个参数
- 效果验证:优化后必须压测验证
- 持续监控:上线后持续观察
- 文档记录:记录所有调优过程和结果
性能调优是科学 + 艺术的结合。科学是方法论和工具,艺术是经验和直觉。持续学习、不断实践,才能成为性能调优高手。