zoukankan      html  css  js  c++  java
  • 服务CPU 100%排查思路

    出处: https://www.cnblogs.com/dennyzhangdd/p/11585971.html

    一、引子

      对于互联网公司,线上CPU飙升的问题很常见(例如某个活动开始,流量突然飙升时),按照本文的步骤排查,基本1分钟即可搞定!特此整理排查方法一篇,供大家参考讨论提高。

    二、问题复现

      线上系统突然运行缓慢CPU飙升,甚至到100%,以及Full GC次数过多,接着就是各种报警:例如接口超时报警等。此时急需快速线上排查问题。

    三、问题排查

      不管什么问题,既然是CPU飙升,肯定是查一下耗CPU的线程,然后看看GC。

    3.1 核心排查步骤

      1.执行“top”命令:查看所有进程占系统CPU的排序。极大可能排第一个的就是咱们的java进程(COMMAND列)。PID那一列就是进程号。

      2.执行“top -Hp 进程号”命令:查看java进程下的所有线程占CPU的情况。

      3.执行“printf "%x 10"命令 :后续查看线程堆栈信息展示的都是十六进制,为了找到咱们的线程堆栈信息,咱们需要把线程号转成16进制。例如,printf "%x 10-》打印:a,那么在jstack中线程号就是0xa.

      4.执行 “jstack 进程号 | grep 线程ID”  查找某进程下-》线程ID(jstack堆栈信息中的nid)=0xa的线程状态。如果“"VM Thread" os_prio=0 tid=0x00007f871806e000 nid=0xa runnable”,第一个双引号圈起来的就是线程名,如果是“VM Thread”这就是虚拟机GC回收线程了

      5.执行“jstat -gcutil 进程号 统计间隔毫秒 统计次数(缺省代表一致统计)”,查看某进程GC持续变化情况,如果发现返回中FGC很大且一直增大-》确认Full GC! 也可以使用“jmap -heap 进程ID”查看一下进程的堆内从是不是要溢出了,特别是老年代内从使用情况一般是达到阈值(具体看垃圾回收器和启动时配置的阈值)就会进程Full GC。

      6.执行“jmap -dump:format=b,file=filename 进程ID”,导出某进程下内存heap输出到文件中。可以通过eclipse的mat(Eclipse Memory Analyzer)工具查看内存中有哪些对象比较多

    3.2 原因分析

    1.内存消耗过大,导致Full GC次数过多

    执行步骤1-5:

    • 多个线程的CPU都超过了100%,通过jstack命令可以看到这些线程主要是垃圾回收线程-》上一节步骤2
    • 通过jstat命令监控GC情况,可以看到Full GC次数非常多,并且次数在不断增加。--》上一节步骤5

    确定是Full GC,接下来找到具体原因

    • 生成大量的对象,导致内存溢出-》执行步骤6,查看具体内存对象占用情况。
    • 内存占用不高,但是Full GC次数还是比较多,此时可能是代码中手动调用 System.gc()导致GC次数过多,这可以通过添加 -XX:+DisableExplicitGC来禁用JVM对显示GC的响应。

    2.代码中有大量消耗CPU的操作,导致CPU过高,系统运行缓慢;

    执行步骤1-4:在步骤4 jstack,可直接定位到代码行。例如某些复杂算法,甚至算法BUG,无限循环递归等等。

    3.由于锁使用不当,导致死锁

    执行步骤1-4: 如果有死锁,会直接提示。关键字:deadlock.步骤四,会打印出业务死锁的位置。

    造成死锁的原因:最典型的就是2个线程互相等待对方持有的锁。

    4.随机出现大量线程访问接口缓慢

      代码某个位置有阻塞性的操作,导致该功能调用整体比较耗时,但出现是比较随机的;平时消耗的CPU不多,而且占用的内存也不高。

    思路:

      首先找到该接口,通过压测工具不断加大访问力度,大量线程将阻塞于该阻塞点。

    执行步骤1-4:

    "http-nio-8080-exec-4" #31 daemon prio=5 os_prio=31 tid=0x00007fd08d0fa000 nid=0x6403 waiting on condition [0x00007000033db000]
    
       java.lang.Thread.State: TIMED_WAITING (sleeping)-》期限等待
    
        at java.lang.Thread.sleep(Native Method)
    
        at java.lang.Thread.sleep(Thread.java:340)
    
        at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386)
    
        at com.*.user.controller.UserController.detail(UserController.java:18)-》业务代码阻塞点

    如上图,找到业务代码阻塞点,这里业务代码使用了TimeUnit.sleep()方法,使线程进入了TIMED_WAITING(期限等待)状态。

     

    5.某个线程由于某种原因而进入WAITING状态,此时该功能整体不可用,但是无法复现;

      执行步骤1-4:jstack多查询几次,每次间隔30秒,对比一直停留在parking 导致的WAITING状态的线程。例如CountDownLatch倒计时器,使得相关线程等待->AQS(AbstractQueuedSynchronizer AQS框架源码剖析)->LockSupport.park()。

    "Thread-0" #11 prio=5 os_prio=31 tid=0x00007f9de08c7000 nid=0x5603 waiting on condition [0x0000700001f89000]   
    java.lang.Thread.State: WAITING (parking) ->无期限等待
    at sun.misc.Unsafe.park(Native Method)    
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:304)    
    at com.*.SyncTask.lambda$main$0(SyncTask.java:8)-》业务代码阻塞点
    at com.*.SyncTask$$Lambda$1/1791741888.run(Unknown Source)    
    at java.lang.Thread.run(Thread.java:748)

    三、总结

      按照3.1节的6个步骤走下来,基本都能找到问题所在。


    另一篇关于CPU的分析: MySQL 如何优化cpu消耗

    目录
    • 谁在消耗cpu?
    • 祸首是谁?
      • 用户
      • IO等待
      • 产生影响
    • 如何减少CPU消耗?
      • 减少等待
      • 减少计算
        • 减少逻辑运算量
        • 减少逻辑IO量
        • 减少query请求量(非数据库本身)
      • 升级cpu

    谁在消耗cpu?

      用户+系统+IO等待+软硬中断+空闲

    祸首是谁?

    用户

    用户空间CPU消耗,各种逻辑运算

    正在进行大量tps
    函数/排序/类型转化/逻辑IO访问...

    用户空间消耗大量cpu,产生的系统调用是什么?那些函数使用了cpu周期?
    参考
    Linux 性能优化解析

    MySQL 几种调式分析利器

    IO等待

    等待IO请求的完成

    此时CPU实际上空闲

    如vmstat中的wa 很高。但IO等待增加,wa也不一定会上升(请求I/O后等待响应,但进程从核上移开了)

    产生影响

    用户和IO等待消耗了大部分cpu

    吞吐量下降(tps)
    查询响应时间增加
    慢查询数增加
    对mysql的并发陡增,也会产生上诉影响

    如何减少CPU消耗?

    减少等待

    减少IO量

    SQL/index,使用合适的索引减少扫描的行数(需平衡索引的正收益和维护开销,空间换时间)

    提升IO处理能力

    加cache/加磁盘/SSD

    减少计算

    减少逻辑运算量

    • 避免使用函数,将运算转移至易扩展的应用服务器中
      如substr等字符运算,dateadd/datesub等日期运算,abs等数学函数
    • 减少排序,利用索引取得有序数据或避免不必要排序
      如union all代替 union,order by 索引字段等
    • 禁止类型转换,使用合适类型并保证传入参数类型与数据库字段类型绝对一致
      如数字用tiny/int/bigint等,必需转换的在传入数据库之前在应用中转好
    • 简单类型,尽量避免复杂类型,降低由于复杂类型带来的附加运算。更小的数据类型占用更少的磁盘、内存、cpu缓存和cpu周期
    • ....

    减少逻辑IO量

    • index,优化索引,减少不必要的表扫描
      如增加索引,调整组合索引字段顺序,去除选择性很差的索引字段等等

    • table,合理拆分,适度冗余
      如将很少使用的大字段拆分到独立表,非常频繁的小字段冗余到“引用表”

    • SQL,调整SQL写法,充分利用现有索引,避免不必要的扫描,排序及其他操作
      如减少复杂join,减少order by,尽量union all,避免子查询等

    • 数据类型,够用就好,减少不必要使用大字段
      如tinyint够用就别总是int,int够用也别老bigint,date够用也别总是timestamp

    • ....

    减少query请求量(非数据库本身)

    • 适当缓存,降低缓存数据粒度,对静态并被频繁请求的数据进行适当的缓存
      如用户信息,商品信息等
    • 优化实现,尽量去除不必要的重复请求
      如禁止同一页面多次重复请求相同数据的问题,通过跨页面参数传递减少访问等
    • 合理需求,评估需求产出比,对产出比极端底下的需求合理去除
    • ....

    升级cpu

    • 若经过减少计算和减少等待后还不能满足需求,cpu利用率还高T_T
    • 是时候拿出最后的杀手锏了,升级cpu,是选择更快的cpu还是更多的cpu了?**
    • 低延迟(快速响应),需要更快的cpu(每个查询只能使用一个cpu)
    • 高吞吐,同时运行很多查询语句,能从多个cpu处理查询中收益

    参考
    《高性能MySQL》
    《图解性能优化》
    大部分整理自《MySQL Tuning For CPU Bottleneck》

  • 相关阅读:
    linux 配置nginx+php-cgi
    有个故事
    2014短学期实习报告
    快速排序
    C++之共用体
    不能言传,等于不会
    java 动态绑定
    编程的理论深度
    Java 多客户端版 2048 源码
    热河看待苦难
  • 原文地址:https://www.cnblogs.com/myseries/p/12894808.html
Copyright © 2011-2022 走看看