引
在分析性能问题时,我们有两种简单而又行之有效的分析方法。第一种是基于资源视角的USE
方法,通过一系列的检查清单来帮助发现瓶颈和错误;第二种方法就是本文要介绍的基于线程视角的TSA
方法。和USE
方法一样,TSA
方法提供了分析问题的起点,帮助我们缩小问题的区域。这种方法可以用在所有的操作系统上,因为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分析例子
pidstat
在原文中,Gregg
介绍了Solaris
系统中的prstat
命令来作为使用TSA
方法的工具:
prstat
在Linux
中,没有该工具,笔者认为可以尝试通过pidstat
来协助我们进行分析:
pidstat -p 34653 -udhr
这是pidstat
一些常用的选项:
- -u:显示CPU使用率、用户空间和内核空间进程的使用率、上下文切换次数、中断次数等信息。
- -r:显示内存使用率、虚拟内存大小、物理内存大小、缺页错误次数等信息。
- -d:显示磁盘I/O使用率、读写速度、I/O操作次数等信息。
- -t:显示进程的线程信息。
- -h:以人类可读的格式显示输出结果。
- -p:指定进程
pid
结果如下图:
以下是对应指标的含义:
- %CPU:进程使用CPU时间的百分比、
- %usr:用户空间进程占用CPU时间的百分比
- %system:内核空间进程占用CPU时间的百分比
- %guest:运行虚拟机的时间占用CPU时间的百分比
- %wait:等待I/O完成的时间占用CPU时间的百分比
- %CPU:进程使用CPU时间的百分比
- minflt/s:每秒钟发生的次缺页错误(minor page faults)的数量
- majflt/s:每秒钟发生的主缺页错误(major page faults)的数量
- VSZ:进程使用的虚拟内存大小(单位为KB)
- RSS:进程使用的物理内存大小(单位为KB)
- %disk read:进程从磁盘读取数据的时间占用CPU时间的百分比
- %disk write:进程向磁盘写入数据的时间占用CPU时间的百分比
- KB_rd/s:每秒钟从磁盘读取的数据量(单位为KB)
- KB_wr/s:每秒钟向磁盘写入的数据量(单位为KB)
- KB_ccwr/s:每秒钟向磁盘写入的数据量,但是被缓存了(单位为KB)
- iodelay:I/O操作的平均延迟时间(单位为毫秒)
- Command:进程的名称
基于pidstat
我们可以初步的做一些分析,查看进程的时间主要花在哪里。更进一步的分析则需要使用更多的工具进行分析。
结论
TSA
方法告诉我们分析性能问题时,可以去了解线程的时间主要花在哪里,然后进行更进一步的分析。其出发点和USE
方法大体一致,也即先了解系统上正在发生什么,再去进行针对性的分析,只不过两者的视角并不一致。
参考资料
- TSA Method(https://www.brendangregg.com/tsamethod.html)