zoukankan      html  css  js  c++  java
  • 性能优化方法论举例(***)

    如何回答性能优化的问题

    90%的人会遇到性能问题,如何用1行代码快速定位?

    性能优化考虑点、工具

    把具体的工具同性能指标结合了起来,同时从不同的层次去描述了性能瓶颈点的分布,实用性和可操作性更强一些。

    系统层的工具分为CPU、内存、磁盘(含文件系统)、网络四个部分,工具集同性能工具(Linux Performance Tools-full)图中的工具基本一致。

    组件层和应用层中的工具构成为:JDK 提供的一些工具 + Trace 工具 + dump 分析工具 + Profiling 工具等。死锁、线程上下文切换

    CPU、线程(top、vmstat、pidstat、jstack)

    基本命令

    和 CPU 相关的指标主要有以下几个。常用的工具有 top、 ps、uptime、 vmstat、 pidstat(单个进程的上下文切换、I/O)等。

    1. CPU利用率(CPU Utilization)       top
    2. CPU 平均负载(Load Average)    top
    3. 上下文切换次数(Context Switch)vmstat
    top - 12:20:57 up 25 days, 20:49,  2 users,  load average: 0.93, 0.97, 0.79
    Tasks:  51 total,   1 running,  50 sleeping,   0 stopped,   0 zombie
    %Cpu(s):  1.6 us,  1.8 sy,  0.0 ni, 89.1 id,  0.1 wa,  0.0 hi,  0.1 si,  7.3 st
    KiB Mem :  8388608 total,   476436 free,  5903224 used,  2008948 buff/cache
    KiB Swap:        0 total,        0 free,        0 used.        0 avail Mem
    
       PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND
    119680 admin     20   0  600908  72332   5768 S   2.3  0.9  52:32.61 obproxy
     65877 root      20   0   93528   4936   2328 S   1.3  0.1 449:03.61 alisentry_cli

    第一行显示的内容:当前时间、系统运行时间以及正在登录用户数。load average 后的三个数字,依次表示过去 1 分钟、5 分钟、15 分钟的平均负载(Load Average)。

    平均负载是指单位时间内,系统处于可运行状态(正在使用 CPU 或者正在等待 CPU 的进程,R 状态)和不可中断状态(D 状态)的平均进程数,也就是平均活跃进程数,CPU 平均负载和 CPU 使用率并没有直接关系。

    第三行的内容表示 CPU 利用率,每一列的含义可以使用 man 查看。CPU 使用率体现了单位时间内 CPU 使用情况的统计,以百分比的方式展示。计算方式为:CPU 利用率 = 1 - (CPU 空闲时间)/ CPU 总的时间。需要注意的是,通过性能分析工具得到的 CPU 的利用率其实是某个采样时间内的 CPU 平均值。注:top 工具显示的的 CPU 利用率是把所有 CPU 核的数值加起来的,即 8 核 CPU 的利用率最大可以到达800%(可以用 htop 等更新一些的工具代替 top)。

    使用 vmstat 命令,可以查看到「上下文切换次数」这个指标,如下表所示,每隔1秒输出1组数据:

    $ vmstat 1
    procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
     r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
     0  0      0 504804      0 1967508    0    0   644 33377    0    1  2  2 88  0  9

    上表的 cs(context switch) 就是每秒上下文切换的次数,按照不同场景,CPU 上下文切换还可以分为中断上下文切换、线程上下文切换和进程上下文切换三种,但是无论是哪一种,过多的上下文切换,都会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,导致系统的整体性能大幅下降。vmstat 的输出中 us、sy 分别用户态和内核态的 CPU 利用率,这两个值也非常具有参考意义。

    vmstat 的输只给出了系统总体的上下文切换情况,要想查看每个进程的上下文切换详情(如自愿和非自愿切换),需要使用 pidstat,该命令还可以查看某个进程用户态和内核态的 CPU 利用率。

    CPU 相关指标异常的分析思路

    1)CPU 利用率:如果我们观察某段时间系统或应用进程的 CPU利用率一直很高(单个 core 超过80%),那么就值得我们警惕了。我们可以多次使用 jstack 命令 dump 应用线程栈查看热点代码,非 Java 应用可以直接使用 perf 进行 CPU 采采样,离线分析采样数据后得到 CPU 执行热点(Java 应用需要符号表进行堆栈信息映射,不能直接使用 perf得到结果)。

    2)CPU 平均负载:平均负载高于 CPU 数量 70%,意味着系统存在瓶颈点,造成负载升高的原因有很多,在这里就不展开了。需要注意的是,通过监控系统监测平均负载的变化趋势,更容易定位问题,有时候大文件的加载等,也会导致平均负载瞬时升高。如果 1 分钟/5 分钟/15 分钟的三个值相差不大,那说明系统负载很平稳,则不用关注,如果这三个值逐渐降低,说明负载在渐渐升高,需要关注整体性能;

    3)CPU 上下文切换:上下文切换这个指标,并没有经验值可推荐(几十到几万都有可能),这个指标值取决于系统本身的 CPU 性能,以及当前应用工作的情况。但是,如果系统或者应用的上下文切换次数出现数量级的增长,就有很大概率说明存在性能问题,如非自愿上下切换大幅度上升,说明有太多的线程在竞争 CPU。

    上面这三个指标是密切相关的,如频繁的 CPU 上下文切换,可能会导致平均负载升高。

    CPU 上的的一些异动,通常也可以从线程上观测到,但需要注意的是,线程问题并不完全和 CPU 相关。与线程相关的指标,主要有下面几个(均都可以通过 JDK 自带的 jstack 工具直接或间接得到):

    1、应用中的总的线程数;
    2、应用中各个线程状态的分布;
    3、线程锁的使用情况,如死锁、锁分布等;

    关于线程,可关注的异常

    1)线程总数是否过多。过多的线程,体现在 CPU 上就是导致频繁的上下文切换,同时线程过多也会消耗内存,线程总数大小和应用本身和机器配置相关;

    2)线程的状态是否异常。观察 WAITING/BLOCKED 线程是否过多(线程数设置过多或锁竞争剧烈),结合应用内部锁使用的情况综合分析;

    3)结合 CPU 利用率,观察是否存在大量消耗 CPU 的线程。

    内存、堆(free、top、vmstat、jmap、cachestat)

    和内存相关的指标主要有以下几个,常用的分析工具有:top、free、vmstat、pidstat 以及 JDK 自带的一些工具。

    1、系统内存的使用情况,包括剩余内存、已用内存、可用内存、缓存/缓冲区;   free
    2、进程(含 Java 进程)的虚拟内存、常驻内存、共享内存;                               top
    3、进程的缺页异常数,包含主缺页异常和次缺页异常;
    4、Swap 换入和换出的内存大小、Swap 参数配置;                                              vmstat
    5、JVM 堆的分配,JVM 启动参数;
    6、JVM 堆的回收,GC 情况。

    使用 free 可以查看系统内存的使用情况和 Swap 分区的使用情况,top 工具可以具体到每个进程,如我们可以用使用 top 工具查看 Java 进程的常驻内存大小(RES),这两个工具结合起来,可用覆盖大多数内存指标。

    下面是使用 free命令的输出:

    $free -h
                  total        used        free      shared  buff/cache   available
    Mem:           125G        6.8G         54G        2.5M         64G        118G
    Swap:          2.0G        305M        1.7G

    重点介绍下 swap 和 buff/cache 这两个指标。

    Swap 的作用是把一个本地文件或者一块磁盘空间作为内存来使用,包括换出和换入两个过程。Swap 需要读写磁盘,所以性能不是很高,事实上,包括 ElasticSearch 、Hadoop 在内绝大部分 Java 应用都建议关掉 Swap,这是因为内存的成本一直在降低,同时这也和 JVM 的垃圾回收过程有关:JVM在 GC 的时候会遍历所有用到的堆的内存,如果这部分内存被 Swap 出去了,遍历的时候就会有磁盘 I/O 产生。Swap 分区的升高一般和磁盘的使用强相关,具体分析时,需要结合缓存使用情况、swappiness 阈值以及匿名页和文件页的活跃情况综合分析。

    buff/cache 是缓存和缓冲区的大小。缓存(cache):是从磁盘读取的文件的或者向磁盘写文件时的临时存储数据,面向文件。使用 cachestat 可以查看整个系统缓存的读写命中情况,使用 cachetop 可以观察每个进程缓存的读写命中情况。缓冲区(buffer)是写入磁盘数据或从磁盘直接读取的数据的临时存储,面向块设备。free 命令的输出中,这两个指标是加在一起的,使用 vmstat 命令可以区分缓存和缓冲区,还可以看到 Swap 分区换入和换出的内存大小。

    常见的内存问题又有哪些

    了解到常见的内存指标后,常见的内存问题又有哪些?总结如下:

    1、系统剩余内存/可用不足(某个进程占用太多、系统本身内存不足),内存溢出;
    2、内存回收异常:内存泄漏(进程在一段时间内内存使用持续走高)、GC 频率异常;
    3、缓存使用过大(大文件读取或写入)、缓存命中率不高;
    4、缺页异常过多(频繁的 I/O 读);
    5、Swap 分区使用异常(使用过大);

    内存相关指标异常后,分析思路是怎么样的

    1、使用 free/top 查看内存的全局使用情况,如系统内存的使用、Swap 分区内存使用、缓存/缓冲区占用情况等,初步判断内存问题存在的方向:进程内存、缓存/缓冲区、Swap 分区;

    2、观察一段时间内存的使用趋势。如通过 vmstat 观察内存使用是否一直在增长;通过 jmap 定时统计对象内存分布情况,判断是否存在内存泄漏,通过 cachetop 命令,定位缓冲区升高的根源等;

    3、根据内存问题的类型,结合应用本身,进行详细分析。

    举例

    使用 free 发现缓存/缓冲区占用不大,排除缓存/缓冲区对内存的影响后 ->

    使用 vmstat 或者 sar 观察一下各个进程内存使用变化趋势 ->

    发现某个进程的内存时候用持续走高 ->

    如果是 Java 应用,可以使用 jmap / VisualVM / heap dump 分析等工具观察对象内存的分配,或者通过 jstat 观察 GC 后的应用内存变化 ->

    结合业务场景,定位为内存泄漏/GC参数配置不合理/业务代码异常等。

    磁盘、文件(iostat、pidstat)

    在分析和磁盘相关的问题时,通常是将其和文件系统同时考虑的,下面不再区分。和磁盘/文件系统相关的指标主要有以下几个,

    常用的观测工具为 iostat和 pidstat,前者适用于整个系统,后者可观察具体进程的 I/O。

    1、磁盘 I/O 利用率:是指磁盘处理 I/O 的时间百分比;
    2、磁盘吞吐量:是指每秒的 I/O 请求大小,单位为 KB;
    3、I/O 响应时间,是指 I/O 请求从发出到收到响应的间隔,包含在队列中的等待时间和实际处理时间;
    4、IOPS(Input/Output Per Second):每秒的 I/O 请求数;
    5、I/O 等待队列大小,指的是平均 I/O 队列长度,队列长度越短越好;

    使用 iostat 的输出界面如下:

    $iostat -dx
    Linux 3.10.0-327.ali2010.alios7.x86_64 (loginhost2.alipay.em14)     10/20/2019     _x86_64_    (32 CPU)
    
    Device:         rrqm/s   wrqm/s     r/s     w/s    rkB/s    wkB/s avgrq-sz avgqu-sz   await r_await w_await  svctm  %util
    sda               0.01    15.49    0.05    8.21     3.10   240.49    58.92     0.04    4.38    2.39    4.39   0.09   0.07

    上图中 %util ,即为磁盘 I/O 利用率,同 CPU 利用率一样,这个值也可能超过 100%(存在并行 I/O);

    rkB/s 和 wkB/s分别表示每秒从磁盘读取和写入的数据量,即吞吐量,单位为 KB;

    磁盘 I/O处理时间的指标为 r_await 和 w_await 分别表示读/写请求处理完成的响应时间;

    svctm 表示处理 I/O 所需要的平均时间,该指标已被废弃,无实际意义。

    r/s + w/s 为 IOPS 指标,分别表示每秒发送给磁盘的读请求数和写请求数;aqu-sz 表示等待队列的长度。

    pidstat 的输出大部分和 iostat 类似,区别在于它可以实时查看每个进程的 I/O 情况。

    如何判断磁盘的指标出现了异常

    1、当磁盘 I/O 利用率长时间超过 80%,或者响应时间过大(对于 SSD,从 0.0x 毫秒到 1.x 毫秒不等,机械磁盘一般为5ms~10ms),通常意味着磁盘 I/O 存在性能瓶颈;

    2、如果 %util 很大,而 rkB/s 和 wkB/s 很小,一般是因为存在较多的磁盘随机读写,最好把随机读写优化成顺序读写,(可以通过 strace 或者 blktrace 观察 I/O 是否连续判断是否是顺序的读写行为,随机读写应可关注 IOPS 指标,顺序读写可关注吞吐量指标);

    3、如果 avgqu-sz 比较大,说明有很多 I/O 请求在队列中等待。一般来说,如果单块磁盘的队列长度持续超过2,一般认为该磁盘存在 I/O 性能问题。

    网络(ping、netstat、ss)

    指标、瓶颈

    网络这个概念涵盖的范围较广,在应用层、传输层、网络层、网络接口层都有不同的指标去衡量。这里我们讨论的「网络」,特指应用层的网络,通常使用的指标如下:

    1、网络带宽:表示链路的最大传输速率;
    2、网络吞吐:表示单位时间内成功传输的数据量大小;
    3、网络延时:表示从网络请求发出后直到收到远端响应,所需要的时间;
    4、网络连接数和错误数;

    一般来说,应用层的网络瓶颈有如下几类:

    1、集群或机器所在的机房的网络带宽饱和,影响应用 QPS/TPS 的提升;
    2、网络吞吐出现异常,如接口存在大量的数据传输,造成带宽占用过高;
    3、网络连接出现异常或错误;
    4、网络出现分区。

    带宽和网络吞吐这两个指标,一般我们会关注整个应用的,通过监控系统可直接得到,如果一段时间内出现了明显的指标上升,说明存在网络性能瓶颈。对于单机,可以使用 sar 得到网络接口、进程的网络吞吐。

    常用命令

    使用 ping 或者 hping3 可以得到是否出现网络分区、网络具体时延

    对于应用,我们更关注整个链路的时延,可以通过中间件埋点后输出的 trace 日志得到链路上各个环节的时延信息。

    使用 netstat、ss 和 sar 可以获取网络连接数或网络错误数。过多网络链接造成的开销是很大的,一是会占用文件描述符,二是会占用缓存,因此系统可以支撑的网络链接数是有限的。

    工具总结

    工具小结

    可以看到的是,在分析 CPU、内存、磁盘等的性能指标时,有几种工具是高频出现的,如 top、vmstat、pidstat,这里稍微总结一下:

    CPU:top、vmstat、pidstat、sar、perf、jstack、jstat;
    内存:top、free、vmstat、cachetop、cachestat、sar、jmap;
    磁盘:top、iostat、vmstat、pidstat、du/df;
    网络:netstat、sar、dstat、tcpdump;
    应用:profiler、dump分析。

    上述的很多工具,大部分是用于查看系统层指标的,在应用层,除了有 JDK 提供的一系列工具,一些商用的产品如 gceasy.io(分析 GC 日志)、fastthread.io(分析线程 dump 日志)也是不错的。

    排查 Java 应用的线上异常或者分析应用代码瓶颈 Arthas

    Arthas 主要面向线上应用实时诊断,解决的是类似「线上应用异常了,需要在线进行分析和定位」的问题,当然,Arthas 提供的一些方法调用追踪工具,对我们排查诸如「慢查询」等问题,也是非常有帮助的。Arthas 提供的主要功能有:

    1、获取线程统计,如线程持有的锁统计、CPU 利用率统计等;
    2、类加载信息、动态类加载、方法加载信息;
    3、调用栈追踪,调用耗时统计;
    4、方法调用参数、结果检测;
    5、系统配置、应用配置信息;
    6、反编译加载类;
    ....

  • 相关阅读:
    alpha冲刺—Day8
    alpha冲刺—Day7
    alpha冲刺—冲刺计划&代码规范
    Linux安装jdk
    chrome插件开发-notification API注意事项
    VSCode远程连接Docker
    Idea发布项目到Docker
    开发环境配置
    Java设计模式之《单例模式》及应用场景
    使用Docker安装jenkins
  • 原文地址:https://www.cnblogs.com/fanguangdexiaoyuer/p/11904659.html
Copyright © 2011-2022 走看看