1.0 性能监控介绍
性能调优是一个寻找系统瓶颈并调节操作系统以消除这些瓶颈的过程。许多系统管理员认为性能调优就像按菜谱做菜一样:简单设置几个系统参数就可以解决一个问题。其实不是这样的,性能调优是在调节操作系统的各个子系统,以期在他们之间取得一种平衡,达到所需的最佳性能。这些子系统包括:CPU、内存、IO、网络。
这些子系统之间高度依赖,其中任意一个子系统出现使用瓶颈都很可能导致其他子系统出现问题。比如:
-
大量的内存读入请求会塞满内存队列
-
大量的网络吞吐能够耗尽CPU资源
-
为了保持内存队列空闲可能会耗尽CPU资源
-
大量内存到磁盘写请求可能耗尽CPU和IO资源
为了调优一个系统,首先必须定位到瓶颈在哪里。有时候看起来是某个子系统出现了问题,但实际可能是另外一个子系统过载引起的。
1.1 判断应用的类型
为了理解从哪里开始调优性能,最重要的是先分析和理解一个系统的表现。应用一般被分为两类:
- IO型:IO型应用会消耗大量的内存及其下的存储系统,这是因为IO型的应用会(在内存中)处理大量的数据。IO型的应用一般来说不会消耗太多的CPU和网络资源(除非存储系统是构建在网络之上的)。IO型的应用一般只使用CPU来产生一个IO请求然后就进入睡眠状态了。数据库通常被认为是一个的IO型应用。
- CPU型:顾名思义,CPU型应用会消耗大量CPU资源。CPU应用需要消耗CPU资源来完成一些批处理工作或者数学运算。大容量的web服务器、邮件服务器以及各种类型的渲染服务器通常被认为是CPU型的应用。
1.2 设定基线数据
因为对系统的预期和定制可能不同,系统性能表现的好坏不能一概而论。因此,先用弄清一个系统的性能预期才能判断该系统是否有性能问题。所以我们需要先设定一个基线数据,即先统计一个可接受性能状态下各项指标的数据。以后可以用该数据来做性能对比。
下面是一个系统基线和高利用率的性能数据对比示例:
看表示CPU空闲率的最后一列(id)我们可以知道基线的CPU空闲率在79%-100%之间。而在第二个图片中我们看到CPU资源没有空闲,被100%占用了。下面需要做的就是判断如此高的CPU占用率是不是预期中的了。
2.0 安装监控工具
大部分Unix系统发布的时候会自带一系列的监控工具,这些监控工具自Unix诞生起就已经成为系统的一部分了。Linux把这些监控工具作为系统的一部分或者附件发行。基本上,所有的Linux发行版本都有包含这些工具的安装包。尽管类似的开源和第三方的监控工具也不少,这篇文章主要还是介绍这些内建工具的使用。
这篇文章将介绍如何应用下列工具来监控系统性能:
Tool | Description | Base | Repository |
vmstat | all purpose performance tool | yes | yes |
mpstat | provide statistics per CPU | no | yes |
sar | all purpose performance monitoring tool | no | yes |
iostat | provides disk statistics | no | yes |
netstat | procides network statistics | yes | yes |
dstat | monitoring statistics aggregator | no | in most distributions |
iptraf | traffic monitoring dashboard | no | yes |
netperf | network bandwidth tool | no | in most distributions |
ethtool | reports on ethernet interface configuration | yes | yes |
iperf | network bandwidth tool | no | yes |
tcptrace | packet analysis tool | no | yes |
3.0 CPU简介
CPU的利用率很大程度取决于运行什么类型的任务。内核调度器服务于两种任务:线程(单或多)和中断。调度器赋予不同任务以不同的优先级,下面是各个任务的优先级排序:
- 中断——设备通知内核完成了某个操作。比如网卡发送了一个网络包或者某个硬件驱动产生了一个IO请求。
- 内核进程——所有的内核进程以此优先级运行。
- 用户进程——即通常所说的用户态。所有的软件应用以用户态运行。在内核调度机制中,用户态优先级最低。
为了便于理解内核如何管理和调度这些任务,下面将介绍一些重点概念:上下文切换、运行队列及利用率。
3.1 上下文切换
大部分现代处理器只能同时运行一个单线程进程或者一个线程。N路超线程或N核处理器则可以同时运行N个线程。其实Linux内核把多核处理器中的每一个核看成一个独立的处理器,例如一个单处理器双核的系统在Linux内核看来就是一个双处理器的系统。
一个标准的Linux内核支持“同时”运行50-50000个线程,如果只有一个CPU,内核必须公平地调度这些线程。每一个线程会被分配一个CPU时间片,一旦线程用完了该时间片或者被更高优先级的任务(比如硬件中断)抢占,线程就会被放回运行队列中,而更高优先级的任务将占用CPU。这种线程之间的切换被称作为上下文切换。
上下文切换时,内核需要消耗一定的资源用于将线程从CPU寄存器移到运行队列。因此,一个系统上下文切换的次数越多,内核用以完成调度工作的额外消耗也就越多。
3.2 运行队列
每一个CPU都维护了一个可运行状态线程的队列。理想状况是调度器一直连续不间断地在执行线程。线程一般要么是在sleep状态(被阻塞或者等待IO完成),要么在可运行状态。在CPU被过度使用时,内核调度器就可能不能立刻执行系统命令,而可运行的线程就可能会充满运行队列。运行队列越大,线程等待执行的时间就可能越长。
一个熟为人知的用于描述运行队列状态的词叫做“负载”。系统的负载的值是正在执行的线程数和等待执行线程数之和。假设,一个双核系统中正有两个线程在执行,还有4个线程在运行队列中等待执行,那么这个系统当前的负载值就是6。Top命令可以统计1/5/15分钟的系统负载均值(即load average)。
3.3 CPU利用率
CPU利用率,即CPU被使用的百分比。CPU的应用情况是衡量一个系统的重要指标。大部分性能监控工具会将CPU的使用分成如下几类:
User Time——CPU用于执行用户态线程的时间百分比
System Time——CPU用于执行内核线程和中断的时间百分比
Wait IO——因为所有线程都在等待IO而CPU处于空闲状态的时间百分比
Idle——CPU完全处于空闲状态的时间百分比
译注:Wait IO和Idle的相同点是线程运行队列为空,不同点是Wait IO是线程等待队列非空而Idle是线程等待队列也为空。
4.0 CPU性能监控
CPU性能表现如何一般从三个方面来衡量:运行队列、利用率和上下文切换。正如前文所提及的,性能表现的好坏和基线数据(或预期)是密不可分的。对大部分系统而言,一些基本的性能预期如下:
- 运行队列——每个处理器运行队列中不应该超过1-3个线程。例如,一个双核的系统中,运行队列长度不应该超过6。(译注:即一个系统的load average值不应该大于核数的4倍。)
- CPU利用率——假如CPU被充分利用了,那么必须达到以下的占比划分:
- User Time占65%-70%
- System Time占30%-35%
- Idle占0%-5%
- 上下文切换——上下文切换的次数和CPU利用率相关。假设CPU利用率达到了上述的占比划分,大量的上下文切换也是可以接受的。
Linux系统有很多工具可以用来统计这些指标。我们将首先来看vmstat和top。
4.1 vmstat工具的使用
vmstat带来的额外性能开销很小,因此,在一个高负载系统上一直运行该工具是可行的,即使你并不想长久地统计它的性能数据。该工具有两种运行模式:统计模式和采样模式。采样模式每隔一个指定的时间间隔会统计和输出一个结果。这种模式在统计一个持久负载下的性能数据时非常有用。下面是一个vmstat在指定时间间隔为1秒时的输出示例:
上面输出中CPU相关各列的意义如下:
列名 | 含义 |
r | 运行队列的长度,即等待执行的线程数目 |
b | 处于阻塞状态或者等待IO完成状态的线程数目 |
in | 系统中断的数目 |
cs | 上下文切换的数目 |
us | CPU执行用户态线程的时间占比 |
sys | CPU执行系统态线程占用的时间占比,包含内核和中断两部分 |
wa | CPU处于等待状态的时间占比(CPU等待状态即所有线程都处于被阻塞或者等待IO完成状态) |
id | CPU处于完全空闲状态的时间占比 |
4.2 案例分析:CPU的持续耗用
在下面的案例中,系统CPU已经被完全用尽。
从上面输出,我们可以得出以下推论:
- 系统中有大量的中断和少数的上下文切换,看起来是某个进程正在请求访问硬件设备。
- CPU用户态耗用占了85%以上,同时只有少量的上下文切换,进一步证明了有一个进程一直在占用CPU。
- 运行队列长度达到可以接受的上限,甚至在几个瞬间已经超过了这个上限。
4.3 案例分析:调度器过载
在下面的案例中,内核调度器一直忙于上下文切换。
从上面的输出,我们可以得出以下推论:
- 上下文切换的次数远大于中断的次数。内核必须消耗大量的时间用于上下文切换。
- 大量的上下文切换导致了CPU利用率的不平衡。从用户态CPU占用极低和Wait IO态CPU占用极高可以明显看出来。
- 因为CPU处于等待IO状态,运行队列开始堆积,等待IO的线程数也开始堆积。
4.4 mpstat工具的使用
如果系统有多个处理器内核,你可以使用mpstat命令来监控各个核。Linux内核把双核处理器看作为两个处理器。因此,一个双核双处理器系统会被认为有4个处理器。mpstat提供了vmstat类似的CPU统计功能,不过mpstat还按CPU核的粒度提供了统计数据。
4.5 案例分析:未充分使用的处理器负载
在下面的案例中,系统有4个CPU内核,有两个CPU耗用型的进程将其中两个核(CPU0和CPU1)充分利用,第三个核正在执行内核和系统调用(CPU3),第四个核(CPU2)处于空闲状态。
Top命令显示了有3个进程(nobody、mysql、apache)几乎各自占用了其中的一整个CPU内核:
你可以通过ps命令的PSR字段判断哪一个进程占用了哪一个CPU内核。
4.6 结论
CPU的性能监控包含如下要点:
- 检查运行队列,保证每个处理器的运行队列长度不超过3。
- 保证CPU的利用率在用户态和系统态的比例在70/30和65/35之间。
- 如果CPU在系统态所花的时间更多,可能不仅仅是过载的原因,尝试重新设置一下进程的优先级
- 运行IO型的进程比运行CPU型的进程更有收益(译注:是指在CPU利用率较高时?)
5.0 虚拟内存相关
虚拟内存使用磁盘作为内存的扩展,这样可用的“内存”就更多了。在内存不够时,内核会把最近没有使用的内存块写到磁盘上去。当这部分内存再次被访问时,会把这部分内容再从磁盘读取到物理内存中。这些操作对用户是完全透明的,Linux上的应用程序只是看到有大量的内存可用,但是不知道这些“内存”有部分是存储在磁盘上的。毫无疑问,读写磁盘比读写真正的内存要慢得多(顺序读写大概比内存慢1000倍),因此程序读写内存的时候,如果使用了大量的虚存,程序运行就慢了。磁盘作为虚存使用的那部分叫做交换空间/分区(swap space)。
5.1 虚存页
虚存是按页进行划分的,X86架构上的虚存页大小是4KB。内核进行虚存读写的时候,是按页进行的。内核在某些时候会将内存页写到交换空间和文件系统中。①
5.2 内存同步②(原文:kernel memory paging)
内存同步是一个常见的操作,不要和内存交换(页置换)搞混淆了。定期将内存同步到磁盘的过程称为内存映射。运行一段时间后,程序可能会慢慢将内存耗尽。在某个时刻,内核为了分配内存给其他程序(或者当前程序),可能需要将最近没有使用的内存置换到磁盘上去,如果之前已经把内存同步到了磁盘上,此时swapping的操作效率就提高了(不需要在swap前将内容同步到磁盘了)。
5.3 内存回收机制
内存回收机制是为了获取可用的物理内存,其挑选牺牲页的算法因内类型而异。一些页类型如下:
Unreclaimable——锁住的、内核使用的、保留的页
Swappable——匿名内存页,和交换分区相关的内存页
Syncable——和文件系统相关的内存页
Discardable——静态页、已废弃的页
除了不可回收页,其他类型页都可能被回收。
内存回收主要的有两大机制:kswapd和“Low on Memory Reclaim”。
5.4 kswapd
守护进程kswapd的作用是保证有一定的空闲内存可用。内核中有pages_high和pages_low两个指标,如果可用内存低于pages_low,kswapd进程开始尝试释放内存,每次释放32页的内存直到可用内存高于pages_high。
kswapd会执行如下的操作:
- 如果页面没有修改,将该页面放到空闲列表
- 如果页面已修改且是和文件系统相关的,将该页面内容写到磁盘 ③
- 如果页面已修改且不是和文件系统相关的(匿名页,对应到交换分区),将该页面内容写到交换设备
5.5 通过pdflush④完成内存同步(原文kernel paging with pdflush)
守护进程pdflush的作用是同步文件系统相关的内存页(Syncable)到磁盘。换句话说,当一个文件的内存副本被修改时,pdflush会将修改写回磁盘。
当内存中的脏页⑤超过10%时,pdflush将同步这些脏页到磁盘,10%这个参数值可由内核的vm.dirty_background_ratio调整。
绝大部分情况下,pdflush和内存回收机制是相互独立的。当内核调用Low on Memory Reclaim时,除了其他释放内存的操作,LMR还调用pdflush来同步过期页。
5.6 案例分析:
vmstat工具在统计CPU使用的同时也统计了虚存的使用情况。下面是vmstat输出中和虚存相关的一些指标:
列名 | 含义 |
swpd | 当前使用的虚存数量,单位KB。当空闲内存达到下限时,会有更多的数据被置换到交换分区。 |
free | RAM当前的可用内存,单位KB。 |
buff | 物理内存作为read()和write()操作缓冲区部分的大小,单位KB。⑥ |
cache | 物理内存映射到进程地址空间的大小,单位KB。⑥ |
so | 写到交换分区的数据量,单位KB |
si | 从交换分区写到RAM的数据量,单位KB |
bo | RAM中置换到文件系统或者交换分区的磁盘块数量 |
bi | 从文件系统或者交换分区置换到RAM中的磁盘块的数量 |
下面vmstat的输出,可以看出在一个IO型应用的尖峰时刻,有大量的虚存使用:
从上面的输出中,我们可以得到以下推论:
大量的磁盘块(bi)从文件系统交换到了内存中。这个可以通过cache的增长明显看出来。
在最后这段时间内,尽管数据从磁盘交换到内存中耗用了RAM,但空闲内存(free)一直保持在17MB。
为了维持可用的空闲内存,kswapd挪用了读写缓存(buff),并将这些内存加入到空闲内存表中。这个可以从buff的减少看出来。
然后kswapd进程将一些脏页写到交换空间(so),这个也可以通过虚存使用(swpd)的增长看出来。
5.7 结论
虚存的性能监控,一般包括以下几点:
- 大缺页越少,响应时间就更快。因为此时系统在使用内存而不是磁盘。
- 空闲内存较低是一个好的现象,表明了缓存正被高效使用,除非同时伴有持续的写交换分区的操作。
- 如果一个系统持续在操作交换分区,表明这个系统内存告紧。
译注:
①内存写到文件系统:比如在释放内存作为buffer使用的部分,需要将内存内容写到文件系统;内存写到交换分区:比如将实存置换到虚存的时候。
②原文中的memory paging指的应该不是内存分页机制,根据上下文指的应该是内存到磁盘的同步,译文中翻译为内存同步
③和文件系统相关的内存页,可能是内存中的buffer或cache部分。内存映射到swap file的部分,不确定是和文件系统相关还是和交换分区相关
④kswapd和pdflush的异同和关联:
相同点:两者都会把内存数据写到磁盘上
不同点:两者把内存数据写到磁盘的目的不同:kswapd是为了释放内存,但释放内存前需要先把这部分数据暂存到磁盘上;pdflush是为了同步内存和磁盘的数据。
关联:正如5.2提到的,pdflush操作会提高kswapd的效率。
⑤原文叫dirty page,一般翻译为脏页,是指缓存比对应的磁盘内容要新的内存页。
⑥原文这里对于的buffer和cache的解释,和很多资料查到的不一致,个人觉得原文这里的解释有点不妥。
buffer:内存用于缓存磁盘块以及文件元数据的部分,这部分数据可以通过块设备和块号直接访问
cache:内存用于缓存文件数据的部分,主要通过inode访问