`
QING____
  • 浏览: 2234185 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

linux系统CPU负载分析(JAVA)

 
阅读更多

    我们的项目通常被部署在linux系统中,在部署项目之前,我们会评估宿主机器所需要的配置,主要是内存、CPU、磁盘、网络等,我们关注点主要在CPU层面。

    1、上线之前,根据业务并发能力峰值(均值)来评估所需CPU的个数,即预计承载能力(配额)。

    2、当程序上线以后,根据监控情况,还要评估资源利用率来决定是否升配或者降配,即实际承载能力结余或者缺额。

    3、程序运行期间,可能有各种各样的突发情况,导致比如CPU瞬间、持续过高,以至于系统服务水平降低。

 

    本文主要通过上述问题逐步展开思考。CPU就像车辆的引擎,非常重要,直接关系到系统的运算能力,反应到我们的实际程序,就是程序的计算速度(不完全是);通俗来讲,我们的宿主机器,CPU个数多、计算频率高,所能支撑的并发能力也就相对较高,反之亦然。

 

    涉及到的主要指令:top、mpstat、pidstat、iostat。

 

    CPU利用率:针对进程,所占用CPU的时间比。其中IO等待不会占用CPU处理时间,但可能引起load过高。

    CPU负载(平均):即常说的load,一段时间内正在使用CPU和等待CPU的平均总任务数(task,即进程);比如单核CPU时load=1,表示此CPU利用率为100%且无任务等待CPU,此时如果load=2则表示利用率为100%且有一个任务在等待。(包含CPU利用率总和 + 等待任务数,比如300%的利用率 = 3)

    我们可以通过top命令查看CPU利用率和load:

 

 

    在top运行页面,输入“1”即可查看每个CPU的利用率。我们需要注意,在任务栏中,%CPU表示当前进程的CPU利用率,此值为多个CPU之和,目前我们基本都是多核CPU,此值可能会大于100%,比如此进程(多任务)使用了2个CPU的60%,那么此时%CPU为120%。

    一个进程瞬时的%CPU过高本身并不会引入太大问题,且整体多个CPU利用率(每个)持续保持一个中位水平(30%)反而是个好事情;不过某单个进程持续的CPU利用率过高时(长时间超过100%),我们需要引起注意,需要排查一下它是否在进行比如持续“并发的进行IO流处理”、较高并发请求处理(线程池)等,此时我们可以适度考虑增加CPU个数。

    还有一种特殊情况,就是一个进程的CPU利用率为100%却发生在一个CPU上,其他CPU空闲。这种情况通常可能是程序的BUG,比如死循环等。还有一种情况“超线程”(hyper threading),单个进程的%CPU利用率在数倍以上,但是整个CPU的%us值却很小(通常单个CPU单位时间内只能处理一个进程指令,超线程可以实现在单个CPU上并发的处理多个任务的指令)。

 

    CPU负载,我们通常关注是平均负载(average load),linux系统支持1分钟、5分钟、15分钟均值,如上图所示top指令的首行,也可以使用“uptime”(mpstat、sar、vmstat都可以完成):

# uptime
 00:09:16 up 408 days, 12:56,  1 user,  load average: 0.02, 0.04, 0.00

    load通常是评估任务并发争抢CPU资源时“激烈”程度:

    1、load = 1,core=1;表示此CPU利用率为100%,无进程任务等待CPU;如果load=1,core=2,等同于其中一个CPU利用率为100%(或者两个CPU之和为100%),另一个空闲,无进程任务等待CPU。

    2、load = 2,core=1;表示此CPU利用率为100%,有一个进程任务在等待CPU;如果load=2,core=2,表示两个CPU的利用率均为100%,无进程等待CPU。

    load的值需要参考CPU的个数(core个数),core=4、load=4则表示4个CPU的利用率都为100%;所以load值是可以大于1的,也完全可能大于cpu个数;但是当load > CPU时,说明“超负载”了,可以考虑适度扩容或者优化。

    (大部分人因为load = cores * 0.7是一个比较合理的load负载值,比如4core,load为2.8比较理想,具体此值的科学性暂不做追究)

 

    我们可以通过“lscpu”指令查看系统的CPU配额:

 

# lscpu
Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
## CPU总虚拟核心数 = Sockets * Core per Socket * Thread per core
CPU(s):                4    
On-line CPU(s) list:   0-3
## 每个实际核心的虚拟核数
Thread(s) per core:    2
## 每个处理器持有的实际核心数
Core(s) per socket:    2
## 处理器个数
Socket(s):             1
NUMA node(s):          1
Vendor ID:             GenuineIntel
CPU family:            6
Model:                 63
Model name:            Intel(R) Xeon(R) CPU E5-2666 v3 @ 2.90GHz
Stepping:              2
CPU MHz:               2893.524
BogoMIPS:              5863.29
Hypervisor vendor:     Xen
Virtualization type:   full
L1d cache:             32K
L1i cache:             32K
L2 cache:              256K
L3 cache:              25600K
NUMA node0 CPU(s):     0-3
 

    多处理器(Multi-Processor):即有多个物理处理器构成的硬件环境。(Sockets)

    多核处理器(Multi-Core-Processor): 即每个物理处理器内部有多个核心。(Core per sockets)

    多线程核心(Multi-thread-Core):每个处理核心支持的并发线程数,也就是常说的虚拟核心数。(Threads per Core)

    处理器个数(CPU):处理器单元的个数,支持的并发处理能力,虚拟核心总数,也是我们常说的CPU个数。

 

    1、CPU利用率不高、但是load很高的问题:我们已知,CPU利用率主要体现在进程对CPU资源的消耗上(计算和调度),但是CPU在资源调度层面其实消耗很少但是通常是处于wait状态,等待资源响应,以磁盘IO为例(包括NFS网络磁盘等),这并不需要消耗太多CPU资源,但是CPU仍然需要等待IO的状态,多任务(进程或者线程)并发的进行IO操作、特别是IO操作明显较慢时,就会出现这种情况。比如我们使用JAVA程序并发的进行多文件读写等。此时我们需要借助其他工具来帮助我们判定问题的根源:

    使用“iostat -txk 1”或者“sar -d 1”(每个一秒采样)来查看:

#iostat -txk 1
12/13/2018 02:34:33 PM
avg-cpu:  %user   %nice %system %iowait  %steal   %idle
           0.79    0.00    0.12    0.02    0.02   99.05

Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await  svctm  %util
xvda              0.00     2.12    0.00    2.75     0.02    48.37    35.13     0.02    8.00   0.67   0.18
xvdb              0.00     0.00    0.00    0.00     0.00     0.44   252.94     0.00    8.21   0.84   0.00

 

    其中%iowait值很高的时候,比如30%以上,我们就需要注意了。iowait表示CPU等待时间与总时间的占比,此值较高表示CPU大量的时间都在等待而不是在做实际工作,说明我们的文件IO部分已经有较大的瓶颈了,对于数据库、存储类型的服务需要关注这些。此外,我们需要明确,网络IO的阻塞通常是不占用iowait。(如果单纯分析进程对磁盘消耗,可以使用iotop指令)

    1)tps:我们基于sar指令,可以看到每个磁盘的tps信息,即每秒IO请求数,不同类型的磁盘所能支持的tps是不同的,tps越高表示磁盘实际处理的请求数越多(此值高可能导致iowait,但是还取决于数据的大小avgrq-sz)。tps高不代表IO效率高,如果TPS高但是IO吞吐量小,说明都是一些小请求,此时我们可能需要优化IO请求。

    2)avgqu-sz:很重要的一个判断指标,表示已接收但尚未被服务的请求数(等待队列数),此值越大,表示IO请求积压的越多,说明IO请求量已经超过了磁盘的实际处理速率,也是判断iowait原因的指标之一,通常avgqu-sz高,iowait也跟着高。我们通常建议大家适度合并IO操作,太多的、小的磁盘IO请求其实是对性能的浪费(avgrq-sze)而且影响磁盘的IO能力。

    3)await:IO请求平均操作时间,此时间包括从请求被接收、队列等待、到实际执行结束;await值越高,反应在进程层面就是IO请求慢,此值越高iowait相应约高。

    4)svctm:IO操作实际被执行的平均时间(不包含等待),通常大量数据IO操作会更耗时。可以对比await一起参考,通常比await小,这两个值比较接近说明IO较少的等待。

    5)%util:很重要的参数,磁盘性能利用率,单位时间内磁盘处理IO所用时间的占比;此值越高,表示磁盘约繁忙,利用率高是个好事情,但是也需要警醒我们注意扩容磁盘个数。此值通常用于判断磁盘效能的瓶颈,如果过高,表示应用对磁盘效能的需求比实际能力要高,考虑升配和扩容吧。但是它本身并不能用于确定iowait。

 

    此外,还有一个更奇怪的现象,就是应用程序没有太多磁盘IO操作,但是CPU iowait仍然很高,这通常是系统内存不足、不断触发内存与swap空间的交换导致,我们需要升级内存或者关闭swap功能(swap,成败萧何也)。我们可以通过“vmstat”来查看swap的交换情况。

 

   此外,我们还需要关注CS次数(通过vmstat查看,CPU上下文切换),有一种情况,就是“cs切换次数很高,load也很高,但是CPU列用率并不是很高”,这种情况在一个网络代理Server或者相类似的组件中偶尔会出现。我们可以通过“pidstat -w” 来查看进程中cs的次数,如果发现有特别高的(每秒几千次),可以考虑先把进程关闭,观察一下load变化。对于java程序,盲目的设置较大的线程池用于处理网络IO操作,也可能导致这种问题的发生。

 

    我们可以通过“iotop -o”找到磁盘高耗的进程,然后在使用上述原则去判断。

 

 

    2、CPU利用率很高,但是load较低的问题:这种情况在“超线程”模式下,是会发生的;此外,如果有限的几个计算密集型的进程,仍然会较高的消耗CPU,而不会导致load较高(但是load肯定高),只要没有等待的任务,此时的load不需要过度担忧。

 

    有关CPU的其他几个指标:

    1、%us:用户态进程CPU占用率(时间)。

    2、%sy:系统内核CPU占用率,此值通常较低,但是有时候开启大量的系统进程,比如shell console进行日志输出、计算,也会导致很高的%sy。有些信息表示,系统级的swap操作也会导致%sy很高,本人未能实际验证。

    3、%wa:基本语义同iowait。

    4、%hi:硬中断,比如网卡、磁盘、键盘等触发的中断请求处理所消耗的CPU占比。

    5、%si:软中断,由应用进程程序触发的中断,对于一些涉及到高并发的网路IO、异步IO(NIO多路复用、AIO等)等应用、代理Server可能需要关注这些。软中断,我们没法避免和消除,也是很频繁和常见的,只需要注意一个事情,默认情况下(云平台优化的另说)si只发生在0号CPU上,所以0号CPU的%si很高(一般也不会特别高,因为si的处理通常都很快速,可能si的次数很大),我们可以通过“service irqbalance start” 开启中断平衡策略,此后si可以在多个CPU之间发生而不是集中在0号。(之所以在一个CPU上,主要是能耗考虑,其他CPU可以休眠节约能耗而不是频换唤醒来处理这种“很小”的中断请求)(vmstat 可以查看有关中断次数)。

 

    对于java而言,老生常谈的问题:如果JAVA服务突发或者持续CPU高耗,怎么发现问题的原因?在此,再啰嗦一遍!

    1、找到高耗的进程:使用“top”指令,然后shift + p,按照CPU利用率排序,持续观察一段时间,找到相应的进程ID,我们假定只有一个java进程。(这种姿势很多,大家选择各自喜欢的命令)

 

 

    2、继续使用top,“top -Hp 37507”

 

    此命令主要是根据PID获取相应的线程ID(十进制),当然也可以继续使用shift + p,按照CPU利用率排序。其中“37588”“37628”持续高耗CPU。 

 

    3、将线程ID,转换成hex:printf '%x\n 37588' ,值为92d4

    4、通过jstack dump线程栈信息:jstack -l 37507 > /tmp/stack.dump,建议多采样几次,线程信息文件很小,几十K,不用担心。

    5、我们通过“vim stack.dump”文件,搜索“92d4”找到线程的执行状态信息:

"SinkRunner-PollingRunner-DefaultSinkProcessor" #33 prio=5 os_prio=0 tid=0x00007f609c003000 nid=0x92d4 runnable [0x00007f60f81fe000]
   java.lang.Thread.State: RUNNABLE
        at sun.nio.ch.NativeThread.current(Native Method)
        at sun.nio.ch.NativeThreadSet.add(NativeThreadSet.java:46)
        at sun.nio.ch.FileChannelImpl.write(FileChannelImpl.java:207)
        - locked <0x00000005df810918> (a java.lang.Object)
        at java.nio.channels.Channels.writeFullyImpl(Channels.java:78)
        at java.nio.channels.Channels.writeFully(Channels.java:101)
        at java.nio.channels.Channels.access$000(Channels.java:61)
        at java.nio.channels.Channels$1.write(Channels.java:174)
        - locked <0x00000005df8107e8> (a java.nio.channels.Channels$1)
        at java.io.OutputStream.write(OutputStream.java:75)
        at org.apache.flume.serialization.BodyTextEventSerializer.write(BodyTextEventSerializer.java:70)
...

    可以看到此线程正在进行文件读取。

    

    此外,我们需要认知一下,JAVA中的线程处于BLOCK/WAITING(对应wait()、sleep()、join()等等)其实不消耗CPU的,但是无休止的、频繁的阻塞、唤醒多线程,仍然可能导致CPU利用率极高,比如粗暴的死循环“Thread.sleep(0)”等。

 

    1、上线之前,我们的web项目部署环境,单机的CPU个数如何评估?

    我们认定,大家的web项目均为分布式部署,但是单台机器的CPU个数该如何评估呢?其实这个事情,没有公式可以计算,因为合理值取决“业务操作类型(高耗还是短频快)”、“并发量(峰值、均值)”、“是否大量访问外部存储系统”、“本地磁盘性能”、“网络效能”等等,甚至老板的任性(过高的、不切实际的夸大业务能力,过于苛刻的SLA要求)都会对机器的配置产生很大影响。

    我们建议:

    1)粗略评估业务均值的并发能力;对于web系统通常是QPS,然后根据接口的平均响应时间,换算成所需并发能力,比如接口处理时间为10ms推导出每秒可以处理100个请求,8Core应该可以支持800 QPS,如果你需要单机支持2000QPS,那么可以暂定至少需要16Core(> 1600QPS)。对于存储类型的系统,我们还需要考虑磁盘IOPS等。

    2)考虑你的业务峰值,尽管我们的系统配置不能按照峰值来配额,但是如果没有合理的消峰策略(限流等),我们仍然可能面临崩溃。

    3)最重要的,就是通过压力测试,来评估你的实际需要,这才是可行的、科学的方式;构建一个与实际环境接近的业务模型和服务集群,通过压测,评估4Core、8Core等等配置下的服务能力,以及集群环境下对整体服务能力的加权等。

    4)对于web系统,我们建议多个中小型配置的机器,优于少量的高配机器。对于存储类型、服务中间件等则另说。

 

    2、我们系统运行如何判断是否决定升配或者降配?

    我们可以通过监控,持续观测系统对CPU的消耗,综合评估业务的增长速率,我们即可评估出当前以及未来一段时间内系统对CPU的实际需要能力。CPU利用率、load就是很直观的数据。我们也可以使用比如“vmstat”、“sar -q”(runq-sz,等待CPU的任务数,持续大于2)等来判断CPU的瓶颈。

 

 

参考:

1、https://www.tecmint.com/understand-linux-load-averages-and-monitor-performance/

2、https://serverfault.com/questions/667078/high-cpu-utilization-but-low-load-average

3、https://unix.stackexchange.com/questions/134381/very-high-cpu-load-but-nothing-significant-in-top

4、https://unix.stackexchange.com/questions/405636/high-load-but-low-cpu-usage

  • 大小: 340.2 KB
  • 大小: 97.1 KB
  • 大小: 107 KB
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics