做 Java 后端开发写业务代码只是其中一部分。真正到了项目上线之后更常见的事情其实不是写新功能而是排查各种线上问题。比如接口突然报错、响应变慢、日志里出现异常、数据库查询超时、服务器资源飙高这些都很常见。很多刚学后端的人会觉得线上问题排查好像很复杂。其实说到底排查问题最重要的不是“会不会背答案”而是有没有一套清晰的思路。这篇文章就结合一个常见场景聊一聊后端开发通常是怎么排查线上问题的。一、先说一个常见场景假设现在有这样一个问题某天测试或者产品突然跑来说订单接口报错了前端提示 500用户无法正常提交订单。这个时候很多人第一反应可能是是不是代码写错了是不是数据库挂了是不是 Redis 出问题了但实际上线上排查最忌讳的就是一上来乱猜。真正比较稳的做法是按顺序一步一步缩小问题范围。二、排查线上问题先记住一个核心思路当接口出问题时通常按这几个方向去排查先确认问题现象再看日志报了什么错再定位是哪一层出了问题再确认是不是环境、配置、依赖或者数据问题最后再决定怎么修很多问题其实并不复杂只是因为排查顺序乱了结果越查越乱。所以真正重要的不是一上来改代码而是先判断这个问题到底发生在哪一层。三、第一步先确认问题现象接口报错不代表你马上就知道错在哪。先要把最基本的信息问清楚哪个接口报错是偶发还是必现所有人都会报错还是只有个别人会报错是测试环境报错还是生产环境报错报错是刚出现的还是一直都有最近有没有发版、改配置、改数据库比如一个订单提交接口报错你至少要先知道请求路径是什么请求参数是什么报错时间大概是什么时候前端返回的是 500、404、401 还是超时问题能不能复现这些信息非常关键。因为有些问题你一听就能大概判断方向如果是 404可能是接口路径或者网关路由问题。如果是 401可能是登录态或者 token 问题。如果是 500大概率是后端代码、数据库、空指针这类异常。如果是超时可能是慢 SQL、远程调用、锁竞争、网络问题。所以排查第一步不是“解决”而是把问题描述清楚。四、第二步先看日志不要先猜后端排查问题时日志永远是第一现场。很多线上问题其实只要你先把日志看了基本就知道问题在哪个方向。常用命令比如tail -f app.log tail -f nohup.out grep ERROR app.log grep Exception app.log grep -C 10 NullPointerException app.log假设订单接口报 500这时候最常见的做法就是先找到报错时间点附近的日志看看有没有异常堆栈。比如tail -n 200 app.log或者grep -C 20 Exception app.log如果日志里直接出现java.lang.NullPointerException那就说明很可能是空指针问题。如果出现org.springframework.dao.DuplicateKeyException那就可能是数据库唯一约束冲突。如果出现org.springframework.dao.DuplicateKeyException那就可能是数据库连接出了问题。如果出现RedisConnectionFailureException那就可能是 Redis 连不上。所以日志的价值就在这里不要先靠想象定位问题先看系统已经告诉了你什么。五、第三步先判断问题在哪一层一般一个后端接口出问题常见排查范围主要有这几层前端请求有没有传对Controller 有没有收到请求Service 业务逻辑有没有异常数据库操作有没有报错Redis、MQ、第三方接口有没有异常服务器资源是不是有问题也就是说排查问题本质上是在回答一个问题到底是哪一层先坏了。比如你看到日志里是空指针那就继续往业务代码里看。如果日志里是 SQL 异常那就去看数据库。如果日志里没异常但接口就是很慢那就去看耗时、线程、SQL、服务器资源。如果接口压根没进服务那就要看网关、Nginx、负载均衡或者接口路径。这一点很重要。很多人排查问题会同时怀疑十几个方向结果最后把自己绕进去了。实际上你只需要先找到第一处异常点。六、一个典型的排查过程这里举一个比较常见的例子。假设订单提交接口突然报 500。1. 先确认能不能复现自己先拿测试参数调一下接口。如果能稳定复现说明问题大概率是真实存在的不是偶发网络抖动。如果不能复现那就要怀疑是不是参数、数据、环境或者并发导致的问题。2. 找对应时间的日志比如用户反馈是下午 3 点 10 分左右报错那就去日志里找这个时间点附近的异常。如果看到了这样的报错java.lang.NullPointerException: Cannot invoke String.equals(Object) because status is null那问题其实已经很清楚了。说明代码里某个 status.equals(...) 执行时status 是空。3. 根据堆栈找到对应代码位置日志里一般会带堆栈信息比如at com.xxx.order.service.OrderService.createOrder(OrderService.java:58)这时候就可以直接定位到代码第 58 行。假设代码是这样的if (status.equals(PAID)) { return; }那么原因就很明显了status 为空调用 equals 导致空指针。4. 再往前追为什么 status 会为空到这里先别急着改代码。还要继续想一个问题status 为什么会是空可能有几种情况前端没传这个参数数据库查出来就是空某个中间处理逻辑把它弄丢了老数据存在脏数据这个时候就要继续结合日志、参数、数据库数据一起看。如果最后发现是数据库某条历史订单的 status 字段本来就是空那问题就不只是“代码没判空”还说明数据本身也不够规范。5. 最后再决定修复方式这种问题最终修复可能就不止一种代码里加空判断修正历史脏数据增加数据库字段约束补充参数校验增加错误日志方便下次排查你会发现真正完整的排查不是只找到一行报错代码而是把“为什么出错”和“以后怎么避免”一起考虑进去。七、如果不是报错而是接口变慢怎么办线上问题不一定都是异常也很常见的一种情况是接口没报错但是响应特别慢。这种时候排查思路会稍微不一样。先看几个方向是只有这个接口慢还是整个系统都慢是偶发慢还是稳定慢是最近刚变慢还是一直都慢是数据库慢还是远程调用慢是 CPU 飙高、内存不够还是线程阻塞常用命令top free -h df -h ps -ef | grep java如果怀疑是 SQL 慢可以看慢 SQL 日志或者直接去数据库执行 SQL 看耗时。如果怀疑是某个远程调用卡住了那就去看接口调用日志。如果怀疑服务器资源不足那就先看 CPU、内存、磁盘、负载。所以“报错”和“变慢”虽然都属于线上问题但排查重点不一样报错优先看异常日志。变慢优先看耗时点和系统资源。八、排查线上问题时我觉得最重要的几个习惯1. 先看日志再动代码很多问题日志里已经写得很明显了。如果日志都没看清楚就去改代码很容易越改越乱。2. 不要凭感觉乱猜线上问题最怕一句话“我觉得可能是这个。”真正靠谱的方式是看现象、看日志、看数据、看调用链再下结论。3. 先定位再修复不要一看到报错就着急改。先确认问题范围再决定修哪里。因为有时候表面报错点并不是根因。4. 注意区分代码问题、数据问题、环境问题有些问题不是代码本身写错了而是配置错了数据脏了数据库连接异常Redis 挂了第三方接口超时服务器资源不够所以不要默认所有问题都是代码问题。5. 修完之后要想想怎么避免下次再发生比如这次是空指针那以后能不能补参数校验加默认值增加判空打更清晰的日志对历史数据做清洗真正成熟一点的排查不只是把 bug 修掉而是顺手把类似问题的重复发生概率降下来。九、后端常见线上问题排查方向总结如果以后再遇到接口异常可以优先从这几个方向看接口报 500重点看应用日志异常堆栈数据库报错空指针参数问题接口响应很慢重点看SQL 是否变慢Redis 是否超时远程调用是否卡住CPU、内存、负载是否异常是否出现锁竞争或线程阻塞接口直接访问不到重点看服务是否启动端口是否监听Nginx 或网关配置防火墙或网络问题接口路径是否正确只有部分用户报错重点看是否和用户数据有关是否和参数有关是否是脏数据导致是否只有特定业务场景才触发这类问题很多时候都不是系统全局问题而是某类数据触发的边界问题。十、总结后端开发排查线上问题真正重要的不是背多少“标准答案”而是有没有一套稳定的排查思路。一个比较常见的排查顺序是先确认问题现象再看日志再判断是哪一层的问题再结合代码、数据、环境继续定位最后再做修复和预防说白了线上问题排查本质上就是不断缩小范围。从“接口报错了”一步步缩小到“哪一行代码、哪条数据、哪个配置、哪个依赖出了问题”。这件事一开始看起来很难但做多了你会发现很多问题其实都有规律。只要顺着日志、现象和调用链往下走大部分问题最后都能找到原因。