线上问题排查指南

2024-08-14 21:09:19 浏览数 (1)

大家好,我是苏三,又跟大家见面了。

前言

最近经常有小伙伴问我,遇到了线上问题要如何快速排查。

这非常考验工作经验了。

有些问题你以前遇到,如果再遇到类似的问题,就能很快排查出导致问题的原因。

但如果某个问题你是第一次遇到,心中可能会有点无从下手的感觉。

这篇文章总结了,我之前遇到过的一些线上问题排查思路,希望对你会有所帮助。

1 OOM问题

OOM问题在生产环境中,一旦出现,一般会是非常严重的问题,服务可能会挂掉。

但是OOM问题有多种情况,不同的情况,出现问题的原因不一样。

1.1 堆内存OOM

服务器的日志一般会打印下面的内容:

代码语言:javascript复制
java.lang.OutOfMemoryError: Java heap space

这种是出现最多的OOM问题。

在Java服务启动时,可以增加下面的参数:

代码语言:javascript复制
-XX: HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=heapdump.hprof

在发生OOM时,程序会自动把当时的内存使用情况,dump保存到指定的文件。

然后使用MAT(Memory Analyzer Tool),或者使用JDK自带的 Java visualvm,来分析dump 文件,找出导致OOM 的代码 。

1.2 栈内存OOM

出现栈内存OOM问题的异常信息如下:

代码语言:javascript复制
java.lang.OutOfMemoryError: unable to create new native thread

如果实际工作中,出现这个问题,一般是由于创建的线程太多,或者设置的单个线程占用内存空间太大导致的。

这个时候需要排查服务的线程数量。

推荐使用线程池,可以减少线程的创建,有效控制服务中的线程数量。

1.3 栈内存溢出

出现栈内存溢出问题的异常信息如下:

代码语言:javascript复制
java.lang.StackOverflowError

该问题一般是由于业务代码中写的一些递归调用,递归的深度超过了JVM允许的最大深度,可能会出现栈内存溢出问题。

如果生产环境中,出现了这个问题,可以排查一下递归调用是否正常,有可能出现了无限递归的情况。

1.4 GC OOM

出现GC OOM问题时异常信息如下:

代码语言:javascript复制
java.lang.OutOfMemoryError: GC overhead limit exceeded

GC OOM一般是由于JVM在GC时,对象过多,导致内存溢出,建议调整GC的策略。

在老代80%时就是开始GC,并且将-XX:SurvivorRatio(-XX:SurvivorRatio=8)和-XX:NewRatio(-XX:NewRatio=4)设置的更合理。

1.5 元空间OOM

出现元空间OOM问题时异常信息如下:

代码语言:javascript复制
java.lang.OutOfMemoryError: Metaspace

JDK8之后使用Metaspace来代替永久代,Metaspace是方法区在HotSpot中的实现。

这个问题一般是由于加载到内存中的类太多,或者类的体积太大导致的。

如果生产环境中出现了这个问题,可以通过下面的命令修改元空间大小:

代码语言:javascript复制
-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m

我在这里列举了OOM问题的最常见的情况,大家如果想了解更多,可以看一下我之前写的一篇文章《工作中最常见的6种OOM问题》,里面有更详细的介绍。

2 CPU100%问题

线上服务出现CPU100%问题,也很常见。

出现这个问题,是由于服务长时间占用CPU资源导致的。

主要原因有下面这几种:

定位这个问题,可以使用JDK自带的jstack工具,或者用阿里开源的Arthas探测工具。

如果对CPU100%问题比较感兴趣,可以看看我的另一篇文章《糟糕,CPU100%了!!!》,里面有更详细的介绍。

3 接口超时问题

不知道你有没有遇到过这样的场景:我们提供的某个API接口,响应时间原本一直都很快,但在某个不经意的时间点,突然出现了接口超时。

导致接口超时的原因有很多,我们需要挨个逐一排查。

下面这张图中给大家列举出现了,生产环境接口突然出现超时问题时的常见原因:

如果大家想进一步了解接口超时问题,可以看看我的另一篇文章《接口突然超时10宗罪。。。

4 索引失效问题

不知道你有没有遇到过,生成环境明明创建了索引,但数据库在执行SQL的过程中,索引竟然失效了。

由于索引失效,让之前原本很快的操作,一下子变得很慢,影响了接口的性能。

我们可以通过explain关键字,查看sql的执行计划,可以确认索引是否失效。

如果索引失效了,可能是哪些原因导致的问题呢?

下面这张图给大家列举了常见原因:

想进一步了解索引失效问题的小伙伴,可以看一下我的另一篇文章《聊聊索引失效的10种场景,太坑了》,里面有非常详细的介绍。

5 死锁问题

如果你使用的是MySQL数据库,在生产环境肯定遇到死锁问题。

死锁是指两个或多个事务在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,这些事务将无法继续向前推进。

在Java中,使用MySQL数据库时,如果遇到MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction异常,意味着数据库检测到了死锁。

MySQL死锁通常由以下原因造成:

  • 资源竞争:多个事务同时竞争相同的资源,比如都试图获取对方持有的锁。
  • 循环等待:事务之间形成了一种互相等待对方释放资源的循环关系。
  • 不当的事务设计:事务执行顺序不合理、执行时间过长等。
  • 并发操作冲突:在高并发环境下,多个事务对同一组数据进行操作,容易引发锁冲突导致死锁。
  • 索引使用不当:如果索引设计不合理,可能导致事务在获取锁时出现问题。

如何减少死锁问题?

  1. 设置合理的事务隔离级别。
  2. 避免大事务的业务代码。
  3. 优化sql性能。
  4. 增加锁等待超时处理。
  5. 增加监控和分析

6 磁盘问题

服务器磁盘问题是众多线上问题中,最好排查的了。

磁盘问题一般有两种:

  1. 磁盘坏了
  2. 磁盘空间不足

如果是磁盘坏了,运维一般在短时间内,很难及时修复好。

因此,需要及时更换磁盘。

如果是磁盘空间不足。

一般需要登录到那台服务器, 使用命令:

代码语言:javascript复制
df -Hl

查看当前服务器的磁盘使用情况。

  • 总大小
  • 已使用多少
  • 可用多少

最快的解决办法是,将/tmp文件夹中的文件删除,可以释放一些磁盘空间。

然后找到日志文件,删除7天以前的日志。

这两种方式,一般会释放不少磁盘空间,暂时解决磁盘空间不足的问题。

从常用来看,我们需要对服务器的磁盘使用情况做监控,如果超过阀值有预警。

同时需要需要规范业务系统,哪些场景需要打印日志,哪些场景不需要,不应该所有的场景,都打印日志。

特别是有些业务查询接口调用非常频繁,一次性返回的数据很多,这种情况下,会导致服务器上的日志迅速膨胀,占用过多的磁盘空间。

7 MQ消息积压问题

如果你使用过MQ消息中间件,在生产环境肯定遇到过MQ消息积压问题。

出现这个问题,一般是MQ消费者消费消息的速度,比MQ生产者生产消息的速度慢。

如果之前一直都是好好的,突然有一天出现了MQ消息积压问题。

可能是下面的原因导致的:

  1. MQ生产者批量发送消息。
  2. 随着数据越来越多,MQ消费者的在处理业务逻辑时,mysql索引失效或者选错索引,导致处理消息的速度变慢。

如果生产环境出现MQ消息积压问题,先确认MQ生产者有没有批量发送消息。

如果有,则可以把MQ消费者中线程池的核心线程数和最大线程数调大一些,让更多的线程去处理业务逻辑,提升消费能力。

这套方案的前提是MQ消费者中,已经使用了线程池消费消息。

如果没有使用线程池,则只能临时增加服务器节点了。

如果MQ生产者没有批量发送消息,则需要排查MQ消费者的业务逻辑中,哪些地方出现了性能问题,需要做代码优化。

优化的方向是:

  1. 优化索引
  2. 优化sql语句
  3. 异步处理
  4. 批量处理

等等,还有其他的。

如果大家对性能优化的技巧比较感兴趣的小伙伴,可以看看我的另一篇文章《我用这11招,让接口性能提升了100倍》,里面有非常详细的介绍。

8 调用接口报错

我们生产环境的程序,有时候会出现,之前调用某个API一直都是正常的,但突然出现报错的情况,即返回码不是200。

那么,这种问题,我们该如何排查呢?

8.1 返回401

一般生产环境出现这个问题,是由于没有通过接口的登录认证。

出现这种情况,一般用户在尝试访问受保护的资源前,需要通过某种形式的身份验证(如登录),但如果未能正确提供必要的认证信息,如Token、用户名和密码等。

就会出现返回码是401的情况。

8.2 返回403

如果生产环境请求某个接口,返回码是403,则说明目前没有访问资源的权限。

这种场景跟返回码是401有区别。

401着重于认证问题,即用户没有提供正确的身份验证信息。

而403则是在认证成功的基础上,用户没有足够的权限去访问请求的资源。

要解决这个问题,我们需要给接口的调用方,分配相应的访问权限。

8.3 返回404

不用怀疑,你请求的接口地址,现在已经不存在了,才会报404。

比如有些接口名称改了,或者接口路径中/v1/user/query改成了/v2/user/query,版本号升级了。

如果没有通知所有的接口调用方,都可能会出现请求接口返回码为404的情况。

还有一种可能也会导致请求接口报404的问题,接口地址之前注册到了API网关中,但API网关的配置出现了问题。

优先排查接口url是否修改,然后排查网关或者Nginx配置是否有问题。

8.4 返回405

如果请求的接口,返回码为405,一般是请求方式错误导致的。

最常见的是:接口只支持post方式,但发送的却是get请求。

或者接口只支持get方式,但发送的却是post请求。

这种问题一般非常好排查和解决。

8.5 返回500

如果请求的接口,返回码为500,一般是出现了服务的内部错误。

一般网关层会对接口的返回值做一次封装,不会返回真正的异常信息。

我们只能查看接口的错误日志,来定位和排查问题。

建议出现异常时,把接口请求参数打印出来,方便后面复现问题。

导致这种问题的原因有很多,我们只能根据服务器上的错误日志,和相关的业务代码逐一排查。

8.6 返回502

如果请求的接口,返回码为502,一般是出现了服务不可用的情况。

有两种情况:

  1. 服务器正在重启中。
  2. 服务挂掉了。

这时候可以查看一下服务的监控,也可以登录到服务器上查看的运行状态。

大部分情况下,重启一下服务,可以快速解决问题。

然后再根据服务器上的日志,可以定位具体的原因,比如:OOM问题导致的。

8.7 返回504

如果请求的接口,返回码为504,一般由于网关或者接口超时导致的。

接口返回数据的耗时,大于网关设置的超时时间,就会出现这个问题。

出现这种情况,一般需要优化接口相关的代码。

如果大家对接口优化,比较感兴趣可以看看我的这篇文章,《我用这11招,让接口性能提升了100倍》,里面有非常详细的介绍。

最后推荐一下我的知识星球:【Java突击队】,干货非常多,目前好评如潮,非常值得一看。星球中包含13大优质专栏:

最近商城项目中更新了:支付宝支付、分库分表、延迟消息、分片算法优化、手写动态定时任务、数据脱敏、动态workId、hanlp敏感词校验,手写分布式ID生成器、分布式限流、手写Mybatis插件、两级缓存提升性能、MQ消息通信、ES商品搜索、OSS服务对接、失败自动重试机制、接口幂等性处理、WebSocket消息推送、百万数据excel导出、用户异地登录检测、freemarker模版邮件发送、代码生成工具、重复请求自动拦截、自定义金额校验注解等等一系列功能。

0 人点赞