线上问题处理流程
一、核心原则与心态
- 保持冷静,数据驱动:切忌盲目猜测。一切结论都应基于日志、监控指标和性能数据。
- 先恢复,后定位:对于严重影响线上服务的问题(如P0级故障),首要目标是快速止损(重启、扩容、降级、熔断),恢复服务,然后再深入排查根因。
- 系统性视角:现代应用是复杂的分布式系统。问题可能出现在应用代码、数据库、中间件、网络、操作系统或硬件等任何环节。需要逐层排查。
- 可观测性是基石:没有完善的监控(Metrics)、日志(Logging)和链路追踪(Tracing),线上问题定位就像盲人摸象。建设好这三大支柱是前提。
二、通用问题定位流程
这是一个从宏观到微观,逐步收敛问题范围的通用流程。
第1步:问题识别与确认
- 目标:确认问题的现象、范围和影响。
- 动作:
- 收到告警(CPU、内存、磁盘、QPS、RT、错误率飙升)。
- 用户反馈(页面打不开、功能报错、响应慢)。
- 查看核心监控大盘:确认是全局性问题还是局部问题?是哪个服务、哪个接口、哪个机房出了问题?
- 初步判断:是性能问题还是功能问题?
第2步:信息收集与取证
- 目标:收集一切可能与问题相关的数据和现场信息。
- 动作:
- 查看日志:迅速查看应用错误日志(
tail -f
,grep ‘ERROR\|Exception’
),寻找明确的错误堆栈信息。这是最快定位问题的方式之一。 - 查看监控:
- 基础设施层:CPU、内存、IO、网络流量。
- 中间件层:数据库连接池、Redis缓存命中率、MQ堆积情况。
- 应用层:QPS、响应时间(RT)、错误率、JVM GC情况(GC频率、耗时、Full GC)。
- 业务层:关键业务指标(如订单创建成功率、支付成功率)。
- 链路追踪:找到一个慢请求或错误请求的TraceID,查看完整的调用链,定位到耗时最长的Span或出错的Span。
- 查看日志:迅速查看应用错误日志(
第3步:分析与定位根因
- 目标:基于收集到的信息,提出假设并验证,找到问题的根本原因。
- 动作:这是最核心的一步,需要根据不同类型的问题使用不同的工具和思路。
- CPU飙升:
top
找到占用CPU最高的进程。top -Hp [pid]
找到该进程中占用CPU最高的线程。printf ‘%x\n’ [tid]
将线程ID转换为16进制。jstack [pid] | grep -A 20 [nid]
查看Java线程堆栈,定位该线程在做什么。常见原因:死循环、频繁GC、计算密集型任务。
- 内存泄漏/OOM:
- 查看JVM监控,观察老年代内存使用率是否持续上升且Full GC无法回收。
- 使用
jmap -histo:live [pid]
查看存活对象 histogram(谨慎使用,会触发Full GC)。 - 使用
jmap -dump:live,format=b,file=heap.hprof [pid]
导出堆内存快照。 - 使用MAT(Eclipse Memory Analyzer)或JProfiler分析hprof文件,找到占用内存最大的对象和引用链。
- 响应时间RT变长:
- 链路追踪定位慢在哪一环。
- 检查数据库慢查询(
slow query log
)。 - 检查外部依赖接口是否超时或变慢。
- 检查是否存在锁竞争(数据库锁、分布式锁、synchronized/ReentrantLock)。
- 错误率飙升:
- 查看错误日志中的异常信息。
- 检查数据库连接池是否占满。
- 检查缓存、MQ等中间件是否可用。
- 检查是否触发了熔断或限流。
- CPU飙升:
第4步:修复与优化
- 目标:实施解决方案。
- 动作:
- 紧急修复:修复代码Bug,紧急发布。
- 配置调整:调整JVM参数、数据库连接池大小、线程池参数等。
- 容量扩容:临时增加机器实例。
- 流程优化:优化慢SQL、添加缓存、重构耗时逻辑。
第5步:复盘与预防
- 目标:避免同类问题再次发生,将经验沉淀。
- 动作:
- 书写事故报告:详细记录故障时间线、根因、修复动作、后续改进项。
- 改进项跟踪:例如:完善监控告警、增加压测、修复代码缺陷、优化架构、完善应急预案等。
- 知识分享:团队内部分享,共同进步。
三、常用工具/命令清单
类型 | Linux命令/工具 | Java生态工具 | 云平台/分布式工具 |
---|---|---|---|
CPU/内存 | top , htop , vmstat , pidstat |
jstack , jmap , jstat |
- |
磁盘IO | iostat , df , du |
- | - |
网络 | netstat , ss , tcpdump , ping |
- | - |
日志 | grep , awk , sed , tail , less |
ELK(Elasticsearch, Logstash, Kibana) | Splunk, Loki |
监控 | - | JMX, Prometheus, Micrometer | Grafana, 云监控(CloudWatch/APM) |
链路追踪 | - | SkyWalking, Zipkin, Jaeger | - |
诊断神器 | strace , perf |
Arthas(阿里巴巴开源的Java诊断利器) | - |
特别推荐 Arthas: 它可以在不重启JVM的情况下,进行动态跟踪、诊断,非常强大。
watch
: 监控方法调用参数、返回值、异常。trace
: 追踪方法内部调用路径,并输出每个节点的耗时。jad
: 反编译线上代码,确认正在运行的版本。dashboard
: 实时查看系统面板,包括线程、内存、GC等信息。
四、优化策略
定位到问题后,常见的优化方向:
- 代码层面:
- 优化算法和数据结构复杂度。
- 避免不必要的对象创建,减少GC压力。
- 使用线程池,避免频繁创建销毁线程。
- 使用连接池(数据库、HTTP)。
- 数据库层面:
- 优化慢SQL(添加索引、优化SQL语句)。
- 读写分离、分库分表。
- 引入缓存(Redis),减少数据库直接压力。
- JVM层面:
- 根据应用特点(CPU密集型 vs IO密集型)和机器配置,合理设置堆大小(
-Xms
,-Xmx
)。 - 选择合适的GC器(如G1,ZGC)。
- 根据应用特点(CPU密集型 vs IO密集型)和机器配置,合理设置堆大小(
- 架构层面:
- 异步化:将非核心逻辑异步处理(通过MQ、线程池)。
- 削峰填谷:用MQ缓冲瞬时流量。
- 熔断降级:防止故障扩散,保证核心链路。
- 弹性伸缩:根据流量自动扩容缩容。
五、实战案例:电商接口RT突然飙升
- 识别:监控大盘发现「商品详情页」接口RT从200ms飙升到2s,错误率轻微上升。
- 收集:
- 日志:grep错误日志,发现大量
TimeoutException
,调用「商品库存服务」超时。 - 监控:发现「商品库存服务」的CPU和RT也同时飙升。
- 链路追踪:通过TraceID查看,耗时全集中在「商品库存服务」的一个
getStockById
方法上。
- 日志:grep错误日志,发现大量
- 分析:
- 登录「商品库存服务」服务器,
top
看到CPU占用高达300%。 top -Hp
找到高CPU线程,jstack
查看堆栈,发现多个线程都卡在执行一条SQL语句上。- 查看数据库慢查询日志,发现该SQL(
SELECT * FROM stock WHERE item_id = ?
)执行了10s+。 - 检查该表,发现
item_id
字段有索引,但表数据量已过亿。 - 根因:虽然 item_id 字段上有索引,但索引失效(比如存在隐式类型转换),而是进行了全表扫描(Full Table Scan)。对于亿级大表,全表扫描的代价是灾难性的。
- 登录「商品库存服务」服务器,
- 修复:
- 短期:紧急扩容库存服务实例,增加数据库连接池,暂时缓解。
- 长期:优化SQL,确保索引有效;考虑按
item_id
进行分库分表;为getStockById
方法添加本地缓存。
- 复盘:
- 改进项:增加数据库慢查询的实时告警;对核心接口进行压测,提前发现容量瓶颈;完善缓存策略。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 技术之路!
评论