大厂做系统性能分析的思路到底是怎样的?
大厂做系统性能分析的思路到底是怎样的?
RESAR性能分析七步法可应用在任何性能分析案例中。有两个关键技术和思路:
- 性能分析决策树
- 性能瓶颈证据链
贯穿整个性能工程的两个重要概念。
如何建性能分析决策树和查找性能瓶颈证据链呢?
1 构建性能分析决策树
性能分析决策树在性能监控设计和性能瓶颈分析时都会被使用,且在性能瓶颈分析时,必须要有决策树思路。
1.1 性能分析决策树是啥
包括系统架构中所有技术组件、所有组件中的模块及模块对应计数器的完整的结构化树状图。
三个重要层级,即组件->模块->计数器。
1.2 步骤
1.2.1 根据系统的架构,罗列整个系统架构中的组件
对应上图,就能罗列出该系统的所有组件:
1.2.2 深入细化组件中的每个重要模块
由于系统组件太多,先选重要组件os为例。因为os 是性能分析中重要一环,几乎所有问题都会体现到os的计数器。
根据os特性而得重要模块:
Swap是为了让系统在无内存可用时,可以用硬盘做内存的交换分区。当Swap被用到时,就说明性能已有问题。所以,一般不建议在性能项目中使用Swap,我们应该在使用Swap之前就把性能问题解决掉。不过,在生产环境中,若被逼无奈,也只能把Swap打开。
其他几个模块基本也是性能分析时必看。
1.2.3 列出模块对应的计数器
罗列计数器时,注意把每个模块重要的计数器都囊括进来,别漏重要的第一级计数器,否则有的数据可能要重跑。
看其一重要模块——CPU。
top查看CPU的几个重要的计数器:
[root@k8s-worker-8 ~]# top
top - 00:38:51 up 28 days, 4:27, 3 users, load average: 78.07, 62.23, 39.14
Tasks: 275 total, 17 running, 257 sleeping, 1 stopped, 0 zombie
%Cpu0 : 4.2 us, 95.4 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.4 st
%Cpu1 : 1.8 us, 98.2 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu2 : 2.1 us, 97.9 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu3 : 1.0 us, 99.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
top有九个计数器:us/sy/ni/id/wa/hi/si/st/load average。前8个是CPU的计数器,load average是啥呢?
load average是CPU的重要的性能计数器,用它判断时,若不能给出明确的判断方向,就有大问题。load average是1m/5m/15m内的可运行状态和不可中断状态的平均进程数:
- 可运行状态。tasks中有个Running状态的任务数。可运行状态不只是它,还有一些万事俱备只差CPU的case。即tasks中的Running状态的任务数,与load average的值之间无直接等价关系
vmstat中,也能看到运行的任务数:
[root@icv-car-receiver-test promtail]# vmstat
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
1 0 0 5067124 178136 1701100 0 0 0 5 2 10 0 0 100 0 0
vmstat的proc列有参数r、b:
- r,正在运行状态和等待运行状态的进程,man手册如是描述:
r: The number of runnable processes (running or waiting for run time).
对【不可中断状态】,经常见到的就是等IO,内存交换也会在这个状态里,这种等IO的情况会体现在vmstat中proc的b列。下面这个计数器就是vmstat的proc的b列的说明。
b: The number of processes in uninterruptible sleep.
所以load average就是vmstat中proc列的r与b之和。
CPU还有两个计数器藏在mpstat,即%guest和%gnice。
[root@k8s-worker-8 ~]# mpstat -P ALL 2
Linux 3.10.0-1062.4.1.el7.x86_64 (icv-car-receiver-test) 04/02/2023 _x86_64_ (4 CPU)
14时00分36秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
14时00分38秒 all 5.13 0.00 3.21 0.00 0.00 0.26 0.00 0.00 0.00 91.40
14时00分38秒 0 4.62 0.00 2.56 0.00 0.00 0.00 0.00 0.00 0.00 92.82
14时00分38秒 1 4.57 0.00 3.05 0.00 0.00 0.00 0.00 0.00 0.00 92.39
14时00分38秒 2 5.70 0.00 3.63 0.00 0.00 0.00 0.00 0.00 0.00 90.67
14时00分38秒 3 5.70 0.00 4.66 0.00 0.00 0.00 0.00 0.00 0.00 89.64
若在宿主机看%guest、%gnice,比较有意义,因为它说明Guest虚拟机消耗CPU的比例。若宿主机上有多个虚拟机,你就能通过这两个参数值,看虚拟机是不是消耗CPU太多,然后通过查进程的方式看具体是哪个虚拟机消耗多。
%guest Show the percentage of time spent by the CPU or CPUs to run a virtual processor.
%gnice Show the percentage of time spent by the CPU or CPUs to run a niced guest.
所以,Linux中:
- 若是宿主机,就要看11个计数器
- 虚拟机,看9个计数器(图中的前9个)即可
要根据Linux操作系统中各模块,把相应计数器全都罗列。所以,其他模块的计数器我们也要这样完整找出。
把Linux所有的关键一级计数器找完后,得图:
这些计数器里,有一些较关键,把重要计数器都标红了。罗列计数器只是个体力活,只要愿意就能列出。
1.2.4 画出计数器之间的相关性
尽管我们列出很多计数器,但这些计数器之间的关系是啥呢。
分析时,要根据相应计数器判断问题方向(有时一个计数器不足支撑作出判断,那就要多个计数器判断)。所以,要画出这些计数器之间的关系。
Linux计数器之间的关系:
线画太多,看着就较乱,所以只画出几个最重要的关键相关性。
Linux的性能分析决策树画完,计数器也覆盖全。还要找到监控工具,收集这些计数器的实时数据。
1.3 收集计数器的实时数据
收集计数器的实时数据时,可能不是一个监控工具就能完全覆盖所有计数器。分析时要清楚监控工具的局限。对Linux监控,最常用prometheus+grafana+node_exporter:
这是经常用的监控Linux的模板。虽覆盖大部分Linux性能计数器,但并不全,如网络的队列、内存的软/硬错误等。
因此,使用监控工具前,一定要把性能分析决策树中的计数器,与监控工具中的计数器对比,缺啥就要在分析时用其他监控工具或命令补充。
用啥监控工具不重要,有没有监控到全部计数器才重要。即便无监控工具,要是只敲命令也能监控到全部计数器,也可。所以,不要迷信工具。
在这个系统的架构中还有其他技术组件,要把这些技术组件都按四步,画相应性能分析决策树,最终形成完整大图。该系统中,若画出全部的技术组件和模块,可得:
至此,就把性能分析决策树完整给你描述完了,步骤也列清。
这只列出第一级性能计数器,若第一级有问题,而我们还不能判断出问题原因,就得继续找下一级。
梳理性能分析决策树时,没必要把所有层级的性能计数器都列出。因为可能整个项目做完,都没用到全部计数器,全列出易浪费时间。看懂第一级计数器,并判断出问题方向,才有可能需看更深层计数器。
所以,理解每个计数器的含义很重要。不理解计数器含义,也不知如何运用计数器,就不可能知道怎么分析。
有性能分析决策树后,如何应用呢?就得看性能瓶颈的证据链。
2 怎么查找性能瓶颈证据链?
若性能分析无证据链,则分析思路是跳跃的,即蒙,根据经验、资料蒙。这种跳跃的分析思路易出错,给人不靠谱感觉,面试官也不喜欢你。分析性能瓶颈要有理有据、顺藤摸瓜。
3 全局监控分析案例
性能分析中,监控分析可分为:
- 全局监控分析
- 定向监控分析
全局监控分析是将整个架构中具概括性的计数器都分析一遍。也只有从全局监控计数器,才能看到性能问题的第一层现象。
比如想找到哪行代码有消耗CPU的问题,CPU计数器会体现CPU使用率高的现象。全局监控就是为查看CPU消耗是不是较高,当看到消耗高时,再往下找是哪行代码消耗较高,就用到定向监控分析的思路。
所以性能分析过程中,分为全局监控分析、定向监控分析两阶段。全局监控分析可用监控平台、命令。
某项目中,有个主机有24颗CPU,场景执行过程中看到:
性能分析决策树中CPU监控的具体命令。分析逻辑:根据性能瓶颈的分析应用,选择相应监控手段,覆盖性能分析决策树中需要监控的计数器,再进一步细化分析。
上图看到所有CPU的%us使用率并未很高,%id也不小,还有剩余。但%si(软中断)这一项,唯独第22颗CPU的%si有21.4%之高。那这软中断合理吗?这也没有太高嘛,能有啥问题?若只看%si平均值,可能确实发现不了问题。仔细看图中更详细数据,就有不一样结论,这也是为何把每颗CPU的使用率都先列出。
当一个应用跑着时,若应用代码消耗很多CPU,那%us的使用率应该变高,但看到的并不是%us高。并且在合理情况下,每个CPU都应该被用上,即这些CPU的使用率应该均衡。但这图中,只有CPU 22的%si使用率较高,为21.4%。并且软中断(%si)只使用了24颗CPU中的一颗。这软中断显然不合理。
4 定向监控分析
那就得知道这软中断到底中断到哪了,为何只中断到一颗CPU?所以,要查看软中断的数据。
Linux有不少工具可查看软中断,如/proc/softirqs文件记录软中断的数据。因为有24颗CPU,数据看起来实在较长。所以,先把一些CPU数据过滤掉,只留下:
CPU 22及其对应的模块名NET_RX。怎么一下子就找到这模块?因为CPU 22的使用率最高,在它上面产生的中断数自然要比其他CPU高得多。由于/proc/softirqs中的各计数器都是累加值,其他模块在各CPU上的累加值比例都没太大差别,只有NET_RX模块在不同CPU的计数值差别很大。可判断:CPU 22的使用率高是因为NET_RX,即网络接收数据。那在网络数据接收的过程中,啥会导致网络的中断只中断在一颗CPU?
网络的接收是靠队列来缓存数据,那就得查网络接收的队列有多少。通过/sys/class/net/<网卡名>/queues/路径,查看到这个网络队列:
RX队列确实只有一个,即所有的网络接收数据都得走这一个队列,自然不可能用到更多CPU,只能用一颗。
深入理解网络中断逻辑可看net_rx_action函数。中断在系统层的调用都是do_softirqs函数,所以perf top -g命令查看CPU热点的时候,你同样也可以看到上面我所描述逻辑。
已知网络接收队列只有一个,那上述问题的解决思路就出来了:多增加几个队列,让更多CPU做中断的事。因为网络中断是为了把数据从网卡向TCP层传输,所以队列一旦变多,传输速度也变得快一些。所以,解决方案就是增加队列:
该例中,把队列增加到8个,网络接收数据就会用到8颗CPU。如果你想用更多CPU也可,这里我们有24颗CPU,就能置24个队列:
- 若你用虚拟机,对这改动,可在KVM的XML参数中增加一个队列参数kvm,就是虚拟机,可参考https://mp.weixin.qq.com/s/L-jNlwYKFWgoKX6L5ffz5A
- 用是物理机,那你就只能换网卡
按这个逻辑,把对应的证据链画出:
根据图中展示逻辑,当看到%si(软中断)高时,就查看cat/proc/interrupts目录。其实对于这软中断,有两个目录可体现:
- cat/proc/interrupts不仅包括软中断,还包括硬中断
- cat/proc/softirqs若看到软中断高,直接看cat/proc/softirqs即可
接着,找到对应模块,再找到这模块的实现原理,最后给解决方案。这样,问题的完整证据链就找到了。
性能瓶颈的证据链其实就是,性能分析决策树在具体应用过程中完整的分析逻辑的记录。
我们一开始不会直接看cat/proc/softirqs,因为这个太定向、太具体。一定要先看全局数据,再一步一步往下找才合理。我们从全局监控分析拿到的数据,和从定向监控分析拿到的数据是不一样的,因为它们是不同的角度。
5 总结
整个分析逻辑总结为:RESAR性能分析七步法,这是性能分析方法论中最重要的核心逻辑。
实际应用时,可按此思路,实现你的性能分析决策树和性能瓶颈证据链。前提是要理解你的系统、架构和分析逻辑。
性能分析决策树的构建及性能瓶颈证据链的查找是我们在每一个性能问题的分析过程中,都必须经历。只有把决策树和证据链具体落地,才能在性能分析无往不利。
6 FAQ
最近测试性能过程,有一次一个查询完成后,发现CPU占用一直居高不下:
- 先jstak PID > PIDstack_log. txt
- 再top -Hp PID 找到占用CPU较高的几个线程
- 通过 printf x% 线程ID 得到16进制的线程ID
- 然后拿到这个16进制的线程ID到PID stack_log. txt中搜索
- 最后发现某块代码导致线程池阻塞没有释放
为何监控了docker,还要监控操作系统Centos?这是不同层面,当然都要监控。何况,docker里也是个完整的操作系统。
一个网络接收队列只会调度到一个cpu的软中断进程处理吗?CPU是CPU,软中断进程是进程,队列是队列,三个东西。因为只有一个队列,所以软中断进程处理时也只能是一个工作线程去处理;因为只有一个工作线程,所以只能是用到一个CPU。
找到对应的模块,然后再找到这个模块的实现原理,是不是需要有开发代码呢?不一定。看栈也能知道个大概,有代码当然是要看代码。
文章转载自公众号: JavaEdge