在分析性能问题时,我们有两种简单而又行之有效的分析方法。第一种是基于资源视角的USE
方法,通过一系列的检查清单来帮助发现瓶颈和错误;第二种方法就是本文要介绍的基于线程视角的TSA
方法。和USE
方法一样,TSA
方法提供了分析问题的起点,帮助我们缩小问题的区域。这种方法可以用在所有的操作系统上,因为TSA
方法的出发点很明确:线程的时间都花在哪里了?
TSA(Thread State Analysis)
方法可以被概括成如下的两个步骤:
分析步骤
这里线程可以指代操作系统可以运行的任何实体,不管是线程、任务还是进程。
线程有多种状态,通常来说我们可以关注如下的六种比较通用的状态:
Executing
):在CPU
上运行;Runnable
):等待被调度到CPU
上运行;Anonymous Paging
):可运行,因为等待匿名换页而被阻塞;Sleeping
):等待I/O
;Lock
):等待拿锁状态;Idle
):等待任务;线程状态
除了这些比较通用的状态,对于可以简单的获取并且很有用的附加状态,我们也可以将它们加入到考虑中。
我们通过下表展示每种状态的含义和针对每种状态的分析方法:
TSA分析方法 - https://www.brendangregg.com/tsamethod.html
简单来说,对于每种状态都有不同的分析思路:
CPU
采样来寻找程序热点;对于内核态运行时间,通过观察系统调用和对内核做采样来寻找热点;CPU
的利用率和饱和度,看看是否有超载的情况存在。此外,检查是否有绑核;下面是这些状态的转移图:
线程状态转移
一般来说,我们没有什么简单的方法来识别空闲的情况。线程可能因为各种原因而在等待运行,此时从内核的角度来看,应用线程处在睡眠或者等待锁阶段,但事实上这个线程可能是处于空闲状态。因此一般情况下,我们可以在分析中跳过空闲状态的分析,我们需要调查空闲状态之后的最常见状态。
我们可以用延迟指标来衡量可运行、匿名换页、睡眠和锁等待状态,通过优化延时,我们可以减少这些状态的时间,甚至将这些状态的时间缩减为0,这种情况下,就可以减少分析的难度,从而更快的进行分析。因此,如果线程有比较多的时间在可运行或者匿名换页状态,我们可以尝试先调整并消除这些状态。
我们前面说的六种状态是最通用的状态,我们可以尝试将它们做更近一步的划分。添加状态可以帮助我们更清晰的了解线程在干什么。比如我们可以做如下的细分:
在实际的状态添加过程中,可能会有一些困难。例如如何衡量睡眠状态的多种子状态消耗的时间。
下面我们会通过一个简短的例子来分析云计算中常见的性能问题,并展示在这个过程中TSA
方法是如何为我们寻找方向的。
我们假设某个应用存在性能问题。遵循TSA
方法,我们测量线程在六种状态下持续的时间。我们发现有大约50%
的时间在Runnable
状态,也即等待被调度到CPU
上运行的状态。基于这个信息,我们将分析方向先放在CPU
上。我们可以使用查看CPU
是否过载的mpstat
命令来观察CPU
是否超载。很快我们可能会发现应用程序的性能瓶颈在CPU
资源限制上,这可能是因为在云计算场景中资源会被限制。因此我们尝试提高CPU
资源限制,就可能会提高性能。
TSA分析例子
在原文中,Gregg
介绍了Solaris
系统中的prstat
命令来作为使用TSA
方法的工具:
prstat
在Linux
中,没有该工具,笔者认为可以尝试通过pidstat
来协助我们进行分析:
pidstat -p 34653 -udhr
这是pidstat
一些常用的选项:
pid
结果如下图:
以下是对应指标的含义:
基于pidstat
我们可以初步的做一些分析,查看进程的时间主要花在哪里。更进一步的分析则需要使用更多的工具进行分析。
TSA
方法告诉我们分析性能问题时,可以去了解线程的时间主要花在哪里,然后进行更进一步的分析。其出发点和USE
方法大体一致,也即先了解系统上正在发生什么,再去进行针对性的分析,只不过两者的视角并不一致。