常见问题排查

发布于:2022-12-27 ⋅ 阅读:(492) ⋅ 点赞:(0)

注意,问题排查应该在线下进行,如果在线上,应该确保此时没有明显对用户产生影响,或者在回滚无法解决问题的时候进行,否则应该优先考虑回滚等手段快速恢复应用而不是排查问题

程序慢的原因

  • IO瓶颈;

  • 内存瓶颈;

  • CPU瓶颈;

各个瓶颈表现:

IO瓶颈:

  • 很多io_wait;

  • iops很高;

  • IO字节数到达磁盘瓶颈;

  • ...

内存瓶颈:

  • 内存使用率很高;

  • GC过于频繁;

  • ...

CPU瓶颈:

  • CPU使用率高;

  • CPU上下文切换频繁;

  • 僵尸线程多;

  • ...

可能的问题:

IO瓶颈:

  • 如果是磁盘IO,有可能是磁盘性能不足、或者写操作突增导致,写操作突增一般是数据库,可以更换更高效的磁盘或者数据库分库分表,各个实例单独部署;

  • 如果是磁盘IO,有可能是数据库读操作有问题,可能有某个sql不停进行全表扫描;

  • 如果是网络IO,有可能是网卡性能不足、或者请求量(用户量)突增导致,可以对带宽进行扩容;

  • 如果是网络IO,有可能是代码BUG导致系统间调用突增,进而导致网卡被打满,可以尝试查看服务间调用量;

  • 如果是网络IO,有可能是应用存在文件上传服务,同时有大量上传请求在处理,导致网卡打满;

内存瓶颈:

  • 内存确实小了,需要增大内存;

  • 应用有系统级缓存,或者用户量突增导致内存占用较大,导致内存不够用,可以尝试对应用弹性扩容;

  • 代码BUG导致内存泄漏;

  • 数据库sql问题,大量进行全表扫描、排序等耗内存操作;

CPU瓶颈:

  • 一般CPU很少成为瓶颈,除非CPU确实性能较差,大多数情况下CPU成为瓶颈都是代码问题,需要检查当前代码有没有死循环;

  • 如果有虚拟内存页,虚拟内存页频繁置换会导致CPU负载高,一般在这时内存使用率也会很高;

  • 数据库sql问题,大量进行排序等耗内存操作;

  • 并发循环IO;

  • 频繁FullGC;

故障快速解决方案

应用BUG

  • 1、首先,我们要排除最近发布干扰,如果最近有发布,则将其回滚(这个根据实际情况来,需要注意不要因为回滚而导致新的问题);

  • 2、如果最近没有发布,则判断该BUG可能是一直存在,只不过最近因为某些原因才触发的,一般有以下几种原因触发:

    • 到达某个特定时间:这个一般是因为程序中有定时任务,这个很好排查,如果定时任务是可停止的,则考虑停止定时任务的执行;

    • 达到指定人数:可能最近系统上正在搞活动,导致访问人数激增,并且接口没有任何措施,最终因为一个点的崩溃导致全系统不可用,一般表现为系统访问越来越慢,最终因为请求堆积导致系统崩溃,这种情况一般重启系统会好,但是过一段时间(甚至在并发高时是立即)系统仍然会变为不可用状态;这种场景比较好确定,影响面通常也比较大,如果确定是因为这个原因,则可以考虑系统/接口限流可以迅速解决,但是后续需要开发来优化代码,提高系统并发能力;

    • 达到指定并发:这种问题一般最难排查,同时也是最诡异的,因为系统平时很稳定,没什么问题,甚至大多数并发场景都没有问题,只有达到特定的并发条件才会触发BUG,线下很难复现,这种问题只能通过相关开发逐步缩小范围,然后检查代码中的并发控制是否到位,找出问题并修复,通常时间修复时间较长,但是影响面一般不会太大;

详细问题排查手段

查询CPU异常飙高

人工排查

1、首先需要确认是哪个进程导致的CPU异常飙高,使用 top命令,然后按字母 t来根据cpu负载对进程排序; 2、确认进程中哪个线程导致的CPU异常飙高,使用top -H -p pid命令查找,其中pid应该替换为上一步查找出来的进程编号,然后同样的按字母t来根据cpu负载对线程排序; 3、确认是哪个进程的哪个线程导致的cpu异常飙高后,使用jstack pid来dump指定进程的线程栈,其中pid就是第一步查询出来的进程号; 4、将第二步查出来的线程号转换为16进制,然后从第三步结果中找到该线程对应的线程栈,这个就是导致CPU飙高的线程栈,可以用它来进行下一步详细分析;

使用脚本自动排查

在CPU异常飙高的机器上执行下面的脚本,自动打印出导致CPU飙高的线程栈:

# topN表示我们要查询CPU占用最高的Java程序中占用CPU最高的N个线程栈,3就表示查询CPU占用最高的Java程序中占用CPU最高的3个线程的线程栈
topN=3
pid=`top -b -n 1 | grep java | awk '{print $9"    "$1}' | sort -nr | head -n 1 | awk '{print $2}'`
tidArr=`top -b -n 1 -H -p ${pid} | grep -E "^\s*[0-9]+" | awk '{print $9"    "$1}' | sort -nr | head -n ${topN} | awk '{print $2}'`

for tid in ${tidArr}
do
    # 查询线程的cpu占用
    cpuUse=`top -b -n 1 -H -p ${pid} | grep -E "^\s*${tid}" | awk '{print $9}'`
    tid=`printf '%x' ${tid}`
    echo "进程[${pid}]的线程[${tid}]cpu 占用为:${cpuUse},详细线程栈如下:"
    
    jstack ${pid} | grep nid=0x${tid} -A 150 | while read line;do if [[ $line != "" ]];then echo $line;else break;fi;done

    echo -e "\n\n"
done

查询慢接口详细信息

根据sky walking快速定位接口慢在哪儿,一般有以下几种情况:

  • DB慢,DB慢有多种原因,一般有如下几种:

    • 慢sql导致的,慢sql一般不会忽然导致接口变慢,只会随着数据量变大而持续增加接口响应时间,除非SQL是新上的;

    • 连接数过小导致的,这个一般很容易被忽略,因为在小并发的场景下一般不会有问题,只有在并发量增大的时候才会导致出现问题,这个很容易排查,可以优先排查下,一般体现在有很多线程hang在db链接获取上(getConnection);

    • DB服务器,近期是否降配,服务器负载是否异常,内存是否够大,文件句柄数是否已优化;

    • 是否从DB获取大量数据,且FetchSize设置过小;

  • 调用其他接口慢,这个需要具体的接口负责人排查;

  • redis慢,redis访问一般不会慢,如果慢,大概率可能是链接不够,都在等链接,或者是网络慢,redis本身慢的概率不会太大;

    • 有可能有人请求了阻塞命令导致redis服务器慢,如keys

    • 排查redis操作是否存在循环IO

如果没有sky walking,那可以使用jstack将堆栈dump下来具体分析(注意,可能需要多dump几次对比分析),如果程序耗时过长是异常的,则从堆栈中可以观察出一些问题,最明显的就是查看下http请求线程都block在哪儿,如果某个点block了特别多线程,那就要考虑看下这块儿代码是不是死锁了、是不是在同步做IO操作等,一般就是这些问题导致了block;如果sql慢,则让运维找出具体的慢sql,开发根据sql查出具体接口,考虑接口禁用掉(实时处理)或者优化(事后处理);

联系我

  • 微信公众号(文章会第一时间更新到公众号):代码深度研究院

  • GitHub:https://github.com/JoeKerouac


网站公告

今日签到

点亮在社区的每一天
去签到