接口性能排查指南
一、排查流程总览
性能排查流程图:
┌─────────────────────────────────────────────────────────────────┐
│ 接口响应慢 │
│ ↓ │
│ ┌────────────────────────────────┐ │
│ │ 1. 确认问题范围 │ │
│ │ - 单个接口 vs 全局 │ │
│ │ - 特定用户 vs 全部用户 │ │
│ └────────────┬───────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────┐ │
│ │ 2. 四维度排查 │ │
│ │ ┌─────┬─────┬─────┬─────┐ │ │
│ │ │ JVM │ SQL │ 网络 │ 线程 │ │ │
│ │ │ GC │ │ I/O │ 池 │ │ │
│ │ └─────┴─────┴─────┴─────┘ │ │
│ └────────────┬───────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────┐ │
│ │ 3. 定位瓶颈并优化 │ │
│ └────────────┬───────────────────┘ │
│ ↓ │
│ ┌────────────────────────────────┐ │
│ │ 4. 验证优化效果 │ │
│ └────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
二、JVM GC 排查
2.1 常用命令
# 查看GC状态(每1秒输出一次)
jstat -gcutil <pid> 1000
# 查看堆内存使用
jmap -heap <pid>
# 生成堆转储文件
jmap -dump:format=b,file=heapdump.hprof <pid>
# 查看线程栈
jstack <pid> > thread_dump.txt
# 查看类加载统计
jstat -class <pid>2.2 GC 日志分析
# JVM启动参数配置GC日志
java -XX:+PrintGCDetails \
-XX:+PrintGCTimeStamps \
-XX:+PrintGCApplicationStoppedTime \
-Xloggc:gc.log \
-jar application.jar2.3 常见GC问题
| 问题类型 | 现象 | 原因 | 解决方案 |
|---|---|---|---|
| 频繁Full GC | 老年代占用高 | 大对象分配、内存泄漏 | 分析堆转储,优化对象生命周期 |
| GC停顿过长 | STW时间超过200ms | 堆内存过大、GC算法不当 | 切换G1GC,调整堆大小 |
| 内存泄漏 | 老年代持续增长 | 对象引用未释放 | 使用MAT分析引用链 |
2.4 工具推荐
| 工具 | 用途 |
|---|---|
| jstat | JVM统计信息 |
| VisualVM | 可视化监控 |
| Arthas | 在线诊断 |
| MAT | 堆转储分析 |
三、慢SQL 排查
3.1 开启慢查询日志
# MySQL配置
slow_query_log = ON
slow_query_log_file = /var/log/mysql/slow.log
long_query_time = 2 # 超过2秒记录
log_queries_not_using_indexes = ON # 记录未使用索引的查询3.2 使用EXPLAIN分析
EXPLAIN SELECT * FROM orders
WHERE store_id = 123
AND create_time > '2024-01-01';3.3 EXPLAIN字段解读
| 字段 | 说明 |
|---|---|
| type | 访问类型(ALL、index、range、ref、eq_ref) |
| key | 使用的索引 |
| rows | 扫描行数 |
| Extra | 额外信息(Using index、Using where、Using filesort) |
3.4 常见SQL优化
-- 优化前:全表扫描
SELECT * FROM orders WHERE status = 'PAID';
-- 优化后:使用索引
CREATE INDEX idx_orders_status ON orders(status);
-- 优化前:深分页问题
SELECT * FROM orders LIMIT 100000, 10;
-- 优化后:使用游标或覆盖索引
SELECT * FROM orders
WHERE id > (SELECT id FROM orders LIMIT 100000, 1)
LIMIT 10;四、网络I/O 排查
4.1 常用命令
# 查看连接状态
netstat -anp | grep <port>
# 抓包分析
tcpdump -i eth0 port 8080 -w capture.pcap
# 网络延迟测试
ping <host>
mtr <host>
# 接口响应时间
curl -w "Time: %{time_total}s\n" http://localhost:8080/api/orders4.2 连接池配置检查
@Configuration
public class DataSourceConfig {
@Bean
public DataSource dataSource() {
HikariCPConfig config = new HikariCPConfig();
config.setMaximumPoolSize(20);
config.setMinimumIdle(5);
config.setConnectionTimeout(30000); // 30秒超时
config.setIdleTimeout(600000); // 10分钟空闲回收
return new HikariDataSource(config);
}
}五、线程池阻塞排查
5.1 线程池监控
public class ThreadPoolMetrics {
private final ThreadPoolExecutor executor;
public ThreadPoolMetrics(ThreadPoolExecutor executor) {
this.executor = executor;
}
public Map<String, Object> getMetrics() {
return Map.of(
"activeThreads", executor.getActiveCount(),
"poolSize", executor.getPoolSize(),
"queueSize", executor.getQueue().size(),
"completedTasks", executor.getCompletedTaskCount(),
"totalTasks", executor.getTaskCount()
);
}
}5.2 常见问题
| 问题 | 现象 | 解决方案 |
|---|---|---|
| 队列积压 | queueSize持续增长 | 增加线程数或优化任务处理时间 |
| 拒绝策略触发 | CallerRunsPolicy执行 | 调整队列大小或拒绝策略 |
| 线程死锁 | activeThreads满但任务不完成 | 检查线程栈分析死锁 |
六、全链路监控
6.1 分布式追踪
// 使用OpenTelemetry
@Autowired
private Tracer tracer;
public Order getOrder(Long orderId) {
try (Span span = tracer.spanBuilder("getOrder").startSpan()) {
span.setAttribute("orderId", orderId);
// ...业务逻辑
}
}6.2 关键指标监控
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| P50/P95/P99 | 响应时间分位值 | P95 > 500ms |
| 错误率 | 请求失败比例 | > 1% |
| QPS | 每秒请求数 | 根据业务调整 |
| 线程池队列长度 | 积压任务数 | > 1000 |
七、面试要点
7.1 核心概念
| 概念 | 说明 |
|---|---|
| GC停顿 | 垃圾回收时应用暂停时间 |
| 慢查询 | 执行时间超过阈值的SQL |
| 连接池 | 数据库连接复用机制 |
| 分布式追踪 | 跨服务请求链路追踪 |
7.2 常见面试问题
Q:如何定位接口响应慢的问题?
A:从四个维度排查:JVM GC、慢SQL、网络I/O、线程池阻塞,使用jstat、EXPLAIN、netstat等工具。
Q:深分页问题如何解决?
A:使用游标、覆盖索引、或禁止深分页查询。
Q:线程池队列满了怎么办?
A:增加最大线程数、使用有界队列配合合理拒绝策略、优化任务处理速度。