一、一句话定位思路
从外向里排查:网络 → 前端 → 网关 → 服务 → 中间件 → 数据库
从简单到复杂:先看监控 → 再查日志 → 最后抓包
二、超时排查流程图
用户报告超时
↓
1. 确认问题范围
├─ 单个用户 ❓ → 检查用户端
├─ 部分用户 ❓ → 检查地域/运营商
└─ 所有用户 ❌ → 服务端问题
↓
2. 检查监控告警
├─ CPU/内存 ❌ → 扩容/重启
├─ 磁盘IO ❌ → 检查日志/磁盘
├─ 网络带宽 ❌ → 检查网络/流量
└─ 都正常 ✓ → 继续排查
↓
3. 查看错误日志
├─ 网关日志 → 限流/熔断?
├─ 服务日志 → 异常/慢SQL?
├─ 中间件日志 → Redis/消息队列?
└─ 数据库日志 → 锁/慢查询?
↓
4. 全链路追踪
├─ 哪个环节耗时?
├─ 哪个服务慢?
└─ 哪个接口慢?
↓
5. 具体问题定位
├─ 代码问题:死循环、GC频繁
├─ 配置问题:超时时间、连接池
├─ 资源问题:内存泄漏、连接泄漏
└─ 外部依赖:第三方接口慢
↓
6. 复现和验证
三、按可能性排序(最有可能在前)
1. 网络问题(最常见!)
1️⃣ DNS解析慢
- 现象:能ping通IP,但域名访问慢
- 检查:nslookup、dig命令
- 解决:DNS缓存、更换DNS
2️⃣ 网络延迟高
- 现象:某些地域/运营商慢
- 检查:traceroute、mtr
- 解决:CDN、多机房部署
3️⃣ 连接数打满
- 现象:新建连接失败
- 检查:netstat、ss命令
- 解决:调整连接数、复用连接
4️⃣ 带宽被打满
- 现象:下载/上传速度慢
- 检查:iftop、nethogs
- 解决:限流、扩容带宽
2. 服务端问题
5️⃣ 数据库慢查询
- 现象:数据库CPU高,接口超时
- 检查:慢查询日志、explain
- 解决:加索引、优化SQL
6️⃣ 缓存失效/穿透
- 现象:Redis响应慢,数据库压力大
- 检查:Redis监控、缓存命中率
- 解决:缓存预热、布隆过滤器
7️⃣ 线程池打满
- 现象:线程等待,任务队列积压
- 检查:线程池监控、JStack
- 解决:调整线程池参数
8️⃣ 频繁Full GC
- 现象:服务暂停,响应变慢
- 检查:GC日志、JVM监控
- 解决:内存调优、代码优化
3. 配置问题
9️⃣ 超时时间设置不合理
- 现象:调用链某个环节超时
- 检查:各服务超时配置
- 解决:合理设置超时时间
🔟 连接池配置不当
- 现象:获取连接超时
- 检查:数据库/Redis连接池
- 解决:调整连接池参数
四、具体排查命令和步骤
步骤1:快速定位问题范围
# 1. 检查是用户问题还是服务问题
# 从不同地方ping/telnet
ping 你的域名
telnet 你的域名 80
# 2. 用curl测试接口
time curl -v http://你的域名/api/test
# 关注:DNS时间、连接时间、SSL时间、首包时间
# 3. 检查网络链路
traceroute 你的域名
mtr 你的域名
步骤2:检查服务状态
# 1. 查看服务是否存活
ps aux | grep java
systemctl status 你的服务
# 2. 检查端口监听
netstat -tlnp | grep 端口
ss -tlnp | grep 端口
# 3. 查看资源使用
top -c
htop
free -h
df -h
iostat -x 1
步骤3:查看日志
# 1. 查看错误日志
tail -f /var/log/你的服务/error.log
grep "timeout" /var/log/你的服务/*.log
grep "ERROR" /var/log/你的服务/*.log
# 2. 查看慢查询日志
tail -f /var/log/mysql/slow.log
mysqldumpslow -s t /var/log/mysql/slow.log
# 3. 查看Nginx/Apache访问日志
tail -f /var/log/nginx/access.log
awk '{print $1}' access.log | sort | uniq -c | sort -nr
步骤4:检查中间件
# 1. Redis状态
redis-cli info | grep -E "(used_memory|connected_clients|instantaneous_ops)"
redis-cli slowlog get 10
# 2. MySQL状态
mysql -e "show processlist;"
mysql -e "show status like 'Threads_connected';"
mysql -e "show variables like '%timeout%';"
# 3. Kafka状态
kafka-topics.sh --list --zookeeper localhost:2181
kafka-consumer-groups.sh --bootstrap-server localhost:9092 --list
五、Java服务专项排查
1. JVM问题排查
# 1. 查看GC情况
jstat -gc pid 1000
jstat -gccause pid 1000
# 2. 查看线程堆栈
jstack pid > thread_dump.log
# 查找:BLOCKED、WAITING状态的线程
# 3. 内存分析
jmap -heap pid
jmap -histo:live pid | head -20
# 4. 生成堆转储(谨慎使用)
jmap -dump:format=b,file=heap.hprof pid
2. 使用Arthas诊断
# 安装Arthas
curl -L https://arthas.aliyun.com/arthas-boot.jar -o arthas-boot.jar
java -jar arthas-boot.jar
# 常用命令
dashboard # 仪表板
thread # 查看线程
watch 类 方法 # 监控方法调用
trace 类 方法 # 追踪方法耗时
profiler start # 开始性能分析
六、全链路追踪排查
1. 使用SkyWalking/Pinpoint
如果接入了全链路追踪,可以:
1. 找到超时的Trace ID
2. 查看调用链,定位慢的span
3. 分析各环节耗时
4. 定位具体慢的方法/服务
2. 手动添加Trace
// 在关键位置添加日志
@RestController
public class OrderController {
@GetMapping("/order/{id}")
public Order getOrder(@PathVariable Long id) {
long start = System.currentTimeMillis();
try {
// 1. 查询数据库
log.info("开始查询数据库");
Order order = orderDao.findById(id);
log.info("数据库查询耗时: {}ms", System.currentTimeMillis() - start);
// 2. 调用其他服务
long rpcStart = System.currentTimeMillis();
userInfo = userService.getUser(order.getUserId());
log.info("用户服务调用耗时: {}ms", System.currentTimeMillis() - rpcStart);
return order;
} finally {
log.info("总耗时: {}ms", System.currentTimeMillis() - start);
}
}
}
七、常见场景和解决方案
场景1:数据库慢查询导致超时
-- 1. 开启慢查询日志
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 2; -- 2秒
-- 2. 查找慢查询
SELECT * FROM mysql.slow_log ORDER BY query_time DESC LIMIT 10;
-- 3. 使用explain分析
EXPLAIN SELECT * FROM orders WHERE user_id = 1001;
-- 4. 优化方案
-- 加索引
CREATE INDEX idx_user_id ON orders(user_id);
-- 优化SQL
-- 分页查询
-- 读写分离
场景2:Redis连接池耗尽
// Spring Boot配置
spring:
redis:
lettuce:
pool:
max-active: 20 # 最大连接数
max-idle: 10 # 最大空闲连接
min-idle: 5 # 最小空闲连接
max-wait: 1000ms # 获取连接最大等待时间
# 监控命令
redis-cli info | grep connected_clients
# 如果connected_clients接近maxclients,需要扩容
场景3:外部接口调用超时
// 合理设置超时时间
@Configuration
public class HttpClientConfig {
@Bean
public RestTemplate restTemplate() {
return new RestTemplateBuilder()
.setConnectTimeout(Duration.ofSeconds(5)) // 连接超时
.setReadTimeout(Duration.ofSeconds(10)) // 读取超时
.build();
}
}
// 使用熔断器
@HystrixCommand(
fallbackMethod = "fallbackMethod",
commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "5000")
}
)
public String callExternalApi() {
// 调用外部接口
}
场景4:线程池配置问题
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10); // 核心线程数
executor.setMaxPoolSize(20); // 最大线程数
executor.setQueueCapacity(100); // 队列容量
executor.setKeepAliveSeconds(60); // 线程空闲时间
executor.setThreadNamePrefix("async-");
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
// 监控线程池
ThreadPoolExecutor executor = (ThreadPoolExecutor) taskExecutor.getThreadPoolExecutor();
System.out.println("活跃线程数: " + executor.getActiveCount());
System.out.println("队列大小: " + executor.getQueue().size());
System.out.println("完成任务数: " + executor.getCompletedTaskCount());
八、面试回答模板
回答结构
**面试官您好,我会按照以下步骤排查服务超时问题:**
**第一步:快速定位问题范围**
1. 确认是单个用户、部分用户还是所有用户的问题
2. 从不同网络环境测试,排除客户端/网络问题
3. 查看监控告警,看CPU、内存、磁盘、网络是否异常
**第二步:检查服务端状态**
1. 查看服务日志,找ERROR或Timeout关键字
2. 检查服务进程状态和资源使用
3. 查看数据库、Redis等中间件状态
**第三步:深度分析(如果前两步没找到)**
1. 使用全链路追踪定位慢的环节
2. 分析慢查询日志,优化SQL
3. 检查线程状态,看是否有死锁或阻塞
4. 分析GC日志,看是否频繁Full GC
5. 检查连接池配置,看是否连接泄漏
**第四步:具体问题定位**
根据排查结果,最常见的问题可能是:
1. **数据库慢查询**(最可能):加索引、优化SQL
2. **外部依赖超时**:设置合理超时、熔断降级
3. **资源不足**:CPU/内存打满,需要扩容
4. **配置问题**:超时时间、连接池配置不当
5. **代码问题**:死循环、内存泄漏
**第五步:解决和预防**
1. 临时解决:重启、扩容、限流
2. 长期解决:优化代码、调整架构
3. 预防措施:完善监控、压测、容量规划
**我的经验是:80%的超时问题都是数据库或外部依赖引起的,**
**所以我会优先排查这两个方向。**
针对不同职级的侧重点
初级工程师:
- 重点:会看日志、会用基本命令
- 回答:我知道要检查日志、监控、数据库
中级工程师:
- 重点:有系统化的排查思路
- 回答:有完整的排查流程,能定位常见问题
高级工程师:
- 重点:能深入分析根本原因
- 回答:能分析JVM、系统内核、网络协议
九、一句话总结
超时排查 = 快速定位范围 + 分层排查 + 工具辅助 + 经验判断
最可能的问题排序:
-
数据库慢查询(索引、SQL优化)
-
外部依赖超时(第三方接口、中间件)
-
资源不足(CPU、内存、连接数)
-
网络问题(DNS、带宽、延迟)
-
配置问题(超时时间、连接池)
记住:先看监控,再查日志,从外向里,从简单到复杂。