性能之巅:洞悉系统、企业与云计算
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

2.5 方法

本节将讲述许多针对系统性能分析和性能调整的方法和步骤,其中一些方法很新,尤其是USE 方法。此外,本节还囊括了一些讹方法(anti-methodologies)。

为了便于总结,这些方法已经被归类成了不同的类型,例如观测分析和实验分析,详见表2.4。

表2.4 通用的系统性能方法

img

续表

img

性能监测、排队理论,以及容量规划会在本章后面部分有所覆盖。后面的各章会在不同的环境中使用这些方法,对于特殊的性能分析领域还会使用一些特定的方法。

从下一节起,将开始进行通用方法的介绍和比较。对于分析性能问题,在使用其他方法之前,你首先应该尝试的方法是问题陈述法。

2.5.1 街灯讹方法

这个方法实际并不是一个深思熟虑的方法。用户选择熟悉的观测工具来分析性能,这些工具可能是从互联网上找到的,或者是用户随意选择的,仅仅想看看会有什么结果出现。这样的方法可能命中问题,也可能忽视很多问题。

性能调整可以用一种试错的方式反复摸索,对所知道的可调参数进行设置,熟悉各种不同的值,看看是否有帮助。

这样的方法也能揭示问题,但当你所熟悉的工具及所做的调整与问题不相关时,进展会很缓慢。这个方法用一类观测偏差来命名,这类偏差叫做街灯效应,出自下面这则寓言:

一天晚上,一个警察看到一个醉汉在路灯下的地面上找东西,问他在找什么。醉汉回答说他钥匙丢了。警察看了看也找不到,就问他:“你确定你钥匙是在这儿丢的,就在路灯下?”醉汉说:“不,但是这儿的光是最亮的”。

这相当于查看top(1),不是因为这么做有道理,而是用户不知道怎么使用其他工具。

用这个方法找到的问题可能是真的问题,但未必是你想要找的那个。有一些其他的方法可以衡量发现的结果,能很快地排除这样的“误报”。

2.5.2 随机变动讹方法

这是一个实验性质的讹方法。用户随机猜测问题可能存在的位置,然后做改动,直到问题消失。为了判断性能是否已经提升,或者作为每次变动结果的判断,用户会选择一项指标进行研究,诸如应用程序运行时间、延时、操作率(每秒操作次数),或者吞吐量(每秒的字节数)。整个方法如下:

1.任意选择一个项目做改动(例如,一项可变参数)。

2.朝某个方向做修改。

3.测量性能。

4.朝另一个方向修改。

5.测量性能。

6.步骤3 或步骤5 的结果是不是要好于基准值?如果是,保留修改并返回步骤1。

这个过程可能最终获得的调整仅适用于被测的工作负载,方法非常耗时而且可能做出的调整不能保持长期有效。例如,一个应用程序的改动规避了一个数据库或者操作系统的bug,其结果是可以提升性能,但是当这个bug 被修复后,程序这样的改动就不再有意义,关键是没有人真正了解这件事情。

做不了解的改动还有另一个风险,即在生产负载的高峰期可能会引发更恶劣的问题,因此还需为此准备一个回退方案。

2.5.3 责怪他人讹方法

这个讹方法包含以下步骤:

1.找到一个不是你负责的系统或环境的组件。

2.假定问题是与那个组件相关的。

3.把问题扔给负责那个组件的团队。

4.如果证明错了,返回步骤1。

也许是网络问题。你能和网络团队确认一下是不是发生了丢包或其他事情么?

不去研究性能问题,用这种方法的人把问题推到了别人身上,当证明根本不是别人的问题时,这对其他团队的资源是种浪费。这个讹方法只是一种因缺乏数据而造成的无端臆想。

为了避免成为牺牲品,向指责的人要屏幕截图,图中应清楚标明运行的是何种工具,输出是怎样中断的。你可以拿着这些东西找其他人征求意见。

2.5.4 Ad Hoc 核对清单法

当需要检查和调试系统时,技术支持人员通常会花一点时间一步步地过一遍核对清单。一个典型的场景,在产品环境部署新的服务器或应用时,技术支持人员会花半天的时间来检查一遍系统在真实压力下的常见问题。该类核对清单是Ad Hoc 的,基于对该系统类型的经验和之前所遇到的问题。

举个例子,这是核对清单中的一项:

运行iostat -x 1 检查await 列。如果该列在负载下持续超过10(ms),那么说明磁盘太慢或是磁盘过载。

一份核对清单会包含很多这样的检查项目。

这类清单能在最短的时间内提供最大的价值,是即时建议(参见2.3 节)而且需要频繁更新以保证反映当前状态。这类清单处理的多是修复方法容易记录的问题,例如设置可调参数,而不是针对源代码或环境做定制的修复。

如果你管理一个技术支持的专业团队,Ad Hoc 核对清单能有效保证所有人都知道如何检查最糟糕的问题,能覆盖到所有显而易见的问题。核对清单能够写得清楚而又规范,说明了如何辨别每一个问题和如何做修复。不过当然,这个清单应该时常保持更新。

2.5.5 问题陈述法

明确问题如何陈述是支持人员开始反映问题时的例行工作。通过询问客户以下问题来完成:

1.是什么让你认为存在性能问题?

2.系统之前运行得好吗?

3.最近有什么改动?软件?硬件?负载?

4.问题能用延时或者运行时间来表述吗?

5.问题影响其他的人和应用程序吗(或者仅仅影响的是你)?

6.环境是怎么样的?用了哪些软件和硬件?是什么版本?是怎样的配置?

询问这些问题并得到相应的回答通常会立即指向一个根源和解决方案。因此问题陈述法作为独立的方法收录在此处,而且当你应对一个新的问题时,首先应该使用的就是这个方法。

2.5.6 科学法

科学法研究未知的问题是通过假设和试验。总结下来有以下步骤:

1.问题

2.假设

3.预测

4.试验

5.分析

问题就是性能问题的陈述。从这点你可以假设性能不佳的原因可能是什么。然后你进行试验,可以是观测性的也可以是实验性的,看看基于假设的预测是否正确。最后是分析收集的试验数据。

举个例子,你可能发现某个应用程序在迁移到一个内存较少的系统时其性能会下降,你假设导致性能不好的原因是较小的文件系统缓存。你可以使用观测的试验方法分别测量两个系统的缓存失效率,预测内存较小的系统缓存失效率更高。用实验的方法可以增加缓存大小(加内存),预测性能将会有所提升。另外,还可以更简单,实验性的测试可以人为地减少缓存的小大(利用可调参数),预计性能将会变差。

下面还有一些更多的例子。

示例(观测性)

1.问题:什么导致了数据库查询很慢?

2.假设:噪声邻居(其他云计算租户)在执行磁盘I/O,与数据库的磁盘I/O 在竞争(通过文件系统)。

3.预测:如果得到在数据库查询过程中的文件系统I/O 延时,可以看出文件系统对于查询很慢是有责任的。

4.试验:跟踪文件系统延时,发现文件系统上等待的时间在整个查询延时中的比例小于5%。

5.分析:文件系统和磁盘对查询速度慢没有责任。

虽然问题还没有解决,但是环境里的一些大型的组件已经被排除了。执行调查的人可以回到第2 步做一个新的假设。

示例(实验性)

1.问题:为什么HTTP 请求从主机A 到主机C 要比从主机B 到主机C 的时间长?

2.假设:主机A 和主机B 在不同的数据中心。

3.预测:把主机A 移动到与主机B 一样的数据中心将修复这个问题。

4.试验:移动主机A 并测试性能。

5.分析:性能得到修复——与假设的一致。

如果问题没有得到解决,在开始新的假设之前,要恢复之前试验的变动!(对于此例,就是把host A 移回去)

示例(实验性)

问题:为什么随着文件系统缓存尺寸变大,文件系统的性能会下降?

假设:大的缓存存放更多的记录,相较于较小的缓存,需要花更多的计算来管理。

预测:把记录的大小逐步变小,使得存放相同大小的数据需要更多的记录,性能会逐渐变差。

试验:用逐渐变小的记录尺寸,试验同样的工作负载。

分析:结果绘图后与预测一致。向下分析现在可以研究缓存管理的程序。

这是一个反向测试——故意损害性能来进一步了解目标系统。

2.5.7 诊断循环

诊断周期与科学方法相似:

假设→仪器检验→数据→假设

就像科学方法一样,这个方法也通过收集数据来验证假设。这个循环强调数据可以快速地引发新的假设,进而验证和改良,以此继续。这与医生看病是很相似的,用一系列小检验来诊断病情,基于每次检验的结果来修正假设。

上述的两个方法,理论和数据都有很好的平衡。从假设发展到数据的过程很快,那么不好的理论就可尽早地被识别和遗弃,进而开发更好的理论。

2.5.8 工具法

工具为导向的方法如下:

1.列出可用到的性能工具(可选的,安装的或者可购买的)。

2.对于每一个工具,列出它提供的有用的指标。

3.对于每一个指标,列出阐释该指标可能的规则。

这个视角的核对清单告诉你哪些工具能用、哪些指标能读,以及怎样阐释这些指标。虽然这相当高效,只依赖可用的(或知道的)工具,就能得到一个不完整的系统视野,但是,与街灯讹方法类似,用户不知道他或她的视野不完整——而且可能自始至终对此一无所知。需要定制工具(如动态跟踪)才能发现的问题可能永远不能被识别并解决。

在实践中,工具法确实在一定程度上辨别出了资源的瓶颈、错误,以及其他类型的问题,但通常不太高效。

当大量的工具和指标可被选用时,逐个枚举是很耗时的。当多个工具有相同的功能时,情况更糟,你要花额外的时间来了解各个工具的优缺点。在某些情况下,比如要选择做文件系统微基准的工具的场合,工具相当多,虽然这时你只需要一个[1]

2.5.9 USE 方法

USE 方法(utilization、saturation、errors)应用于性能研究,用来识别系统瓶颈[Gregg 13]。一言以蔽之,就是:

对 于所有的资源,查看它的使用率、饱和度和错误。

这些术语定义如下。

● 资源:所有服务器物理元器件(CPU、总线……)。某些软件资源也能算在内,提供有用的指标。

● 使用率:在规定的时间间隔内,资源用于服务工作的时间百分比。虽然资源繁忙,但是资源还有能力接受更多的工作,不能接受更多工作的程度被视为饱和度。

● 饱和度:资源不能再服务更多额外工作的程度,通常有等待队列。

● 错误:错误事件的个数。

某些资源类型,包括内存,使用率指的是资源所用的容量。这与基于时间的定义是不同的,这在之前的2.3.11 节做过解释。一旦资源的容量达到100%的使用率,就无法接受更多的工作,资源或者会把工作进行排队(饱和),或者会返回错误,用USE 方法也可以予以鉴别。[2]

错误需要调查,因为它们会损害性能,如果故障模式是可恢复的,错误可能难以立即察觉。这包括操作失败重试,还有冗余设备池中的设备故障。

与工具法相反的是,USE 方法列举的是系统资源而不是工具。这帮助你得到一张完整的问题列表,在你寻找工具的时候做确认。即便对于有些问题现有的工具没有答案,但这些问题所蕴含的知识对于性能分析也是极其有用的:这些是“已知的未知”。

USE 方法会将分析引导到一定数量的关键指标上,这样可以尽快地核实所有的系统资源。在此之后,如果还没有找到问题,那么可以考虑采用其他的方法。

过程

图2.12 描绘了USE 方法的流程图。错误被置于检查首位,要先于使用率和饱和度。错误通常容易很快被解释,在研究其他指标之前,把它们梳理清楚是省时高效的。

img

图2.12 USE 方法流程

这个方法辨别出的很可能是系统瓶颈问题。不过,一个系统可能不只面临一个性能问题,因此你可能一开始就能找到问题,但所找到的问题并非你关心的那个。在根据需要返回USE 方法遍历其他资源之前,每个发现可以用更多的方法进行调查。

指标表述

USE 方法的指标通常如下。

● 使用率:一定时间间隔内的百分比值(例如,“单个CPU 运行在90%的使用率上”)。

● 饱和度:等待队列的长度(例如,“CPU 的平均运行队列长度是4”)。

● 错误:报告出的错误数目(例如,“这个网络接口发生了50 次滞后冲突”)。

虽然看起来有点违反直觉,但即便整体的使用率在很长一段时间都处于较低水平,一次高使用率的瞬时冲击还是能导致饱和与性能问题的。某些监控工具汇报的使用率是超过5 分钟的均值。举个例子,每秒的CPU 使用率可以变动得非常剧烈,因此5 分钟时长的均值可能会掩盖短时间内100%的使用率,甚至是饱和的情况。

想象一下高速公路的收费站。使用率就相当于有多少收费站在忙于收费。使用率100%意味着你找不到一个空的收费站,必须排在别人的后面(饱和的情况)。如果我说一整天收费站的使用率是40%,你能判断当天是否有车在某一时间排过队吗?很可能在高峰时候确实排过队,那时的使用率是100%,但是这在一天的均值上是看不出的。

资源列表

USE 方法的第一步是要建一张资源列表,要尽可能完整。下面是一张服务器通常的资源列表,配有相应的例子。

● CPU:插槽、核、硬件线程(虚拟CPU)

● 内存:DRAM

● 网络接口:以太网端口

● 存储设备:磁盘

● 控制器:存储、网络

● 互联:CPU、内存、I/O

每个组件通常作为一类资源类型。例如,内存是一种容量资源,网络接口是一类I/O 资源(IOPS 或吞吐量)。有些组件体现出多种资源类型:例如,存储设备既是I/O 资源也是容量资源。这时需要考虑到所有的类型都能够造成性能瓶颈,同时,也要知道I/O 资源可以进一步被当作排队系统来研究,将请求排队并被服务。

某些物理资源,诸如硬件缓存(如CPU 缓存),可能不在清单中。USE 方法是处理在高使用率或饱和状态下性能下降的资源最有效的方法,当然还有其他的检测方法。如果你不确定清单是否该包括一项资源,那就包括它,看看在实际指标中是什么样的情况。

原理框图

另一种遍历所有资源的方法是找到或者画一张系统的原理框图,正如图2.13 所示的那样。这样的图还显示了组件的关系,这对寻找数据流动中的瓶颈是很有帮助的。

CPU、内存、I/O 互联和总线常常被忽视。所幸的是,它们不是系统的常见瓶颈,因为这些组件本身就设计有超过吞吐量的余量。可能你需要升级主板,或者减小负载,例如,“零拷贝”就减轻了内存和总线的负载。

要了解互联的内容,参见第6章6.4.1 节中的CPU 性能计数器介绍。

指标

一旦你掌握了资源的列表,就可以考虑这三类指标:使用率、饱和度,以及错误。表2.5列举了一些资源和指标类型,以及一些可能的指标(针对一般性的OS)。

这些指标要么是一定时间间隔的均值,要么是累计数目。

img

图2.13 双CPU 系统原理框图示例

表2.5 USE 方法指标示例

img

续表

img

重复所有的组合,包括获取每个指标的步骤,记录下当前无法获得的指标,那些是已知的未知。最终你得到一个大约30 项的指标清单,有些指标难以测量,有些根本测不了。所幸的是,常见的问题用较简单的指标就能发现(例如,CPU 饱和度、内存容量饱和度、网络接口使用率、磁盘使用率),所以这些指标要首先测量。

一些较难的组合示例可见表2.6。

表2.6 USE 方法指标的进阶示例

img

上述的某些指标可能用操作系统的标准工具是无法获得的,可能需要使用动态跟踪或者用到CPU 性能计数工具。

附录A 是针对Linux 系统的一个USE 方法核对清单的范例,囊括了硬件资源和Linux 观测工具集的所有组合。附录B 提供的内容一样,不过针对的是基于Solaris 的系统。两个附录还包含了一些软件资源。

软件资源

某些软件资源的检测方式可能相似。这里指的是软件组件,而不是整个应用程序,示例如下。

● 互斥锁:锁被持有的时间是使用时间,饱和度指的是有线程排队在等锁。

● 线程池:线程忙于处理工作的时间是使用时间,饱和度指的是等待线程池服务的请求数目。

● 进程/线程容量:系统的进程或线程的总数是有上限的,当前的使用数目是使用率,等待分配认为是饱和度,错误是分配失败(例如,“cannot fork”)。

● 文件描述符容量:同进程/线程容量一样,只不过针对的是文件描述符。

如果这些指标在你的案例里管用,就用它们;否则,用其他方法也是可以的,诸如延时分析。

使用建议

对于使用上述这些指标类型,这里有一些总体的建议。

● 使用率:100%的使用率通常是瓶颈的信号(检查饱和度并确认其影响)。使用率超过60%可能会是问题,基于以下理由:时间间隔的均值,可能掩盖了100%使用率的短期爆发,另外,一些资源,诸如硬盘(不是CPU),通常在操作期间是不能被中断的,即使做的是优先级较高的工作。随着使用率的上升,排队延时会变得更频繁和明显。有更多关于60%使用率的内容,参见2.6.5 节。

● 饱和度:任何程度的饱和都是问题(非零)。饱和程度可以用排队长度或者排队所花的时间来度量。

● 错误:错误都是值得研究的,尤其是随着错误增加性能会变差的那些错误。

低使用率、无饱和、无错误:这样的反例研究起来容易。这点要比看起来还有用——缩小研究的范围能帮你快速地将精力集中在出问题的地方,判断其不是某一个资源的问题,这是一个排除法的过程。

云计算

在云计算环境,软件资源控制在于限制或给分享系统的多个租户设定阈值。在Joyent 公司我们主要用OS 虚拟技术(SmartOS Zones),来设定内存限制、CPU 限制,以及存储I/O 的门限值。每一项资源的限定都能用USE 方法来检验,与检查物理资源的方法类似。

举个例子,“内存容量使用率”是租户的内存使用率与他的内存容量的比值。“内存容量饱和”出现于匿名的分页活动,虽然传统的页面扫描器此时可能是空闲的。

2.5.10 工作负载特征归纳

工作负载特征归纳是辨别这样一类问题简单而又高效的方法——由施加的负载导致的问题。这个方法关注于系统的输入,而不是所产生的性能。你的系统可能没有任何架构或配置上的问题,但是系统的负载超出了它所能承受的合理范围。

工作负载可以通过回答下列的问题来进行特征归纳:

● 负载是产生的?进程ID、用户ID、远端的IP 地址?

● 负载为什么会被调用?代码路径、堆栈跟踪?

● 负载的特征是什么?IOPS、吞吐量、方向类型(读取/写入)?包含变动(标准方差),如果有的话。

● 负载是怎样随着时间变化的?有日常模式吗?

将上述所有的问题都做检查会很有用,即便你对于答案会是什么已经有很强的期望,但还是应做一遍,因为你很可能会大吃一惊。

请思考这么一个场景:你碰到一个数据库的性能问题,数据库请求来自一个Web 服务器池。你是不是应该检查正在使用数据库的IP 地址?你本认为这些应该都是Web 服务器,正如所配置的那样。但你检查后发现好像整个因特网都在往数据库扔负载,以摧毁其性能。你正处于拒绝服务(denial-of-service,DoS)攻击中!

最好的性能来自消灭不必要的工作。有时候不必要的工作是由于应用程序的不正常运行引起的,例如,一个困在循环的线程无端地增加CPU 的负担。不必要的工作也有可能来源于错误的配置——举个例子,在白天运行全系统的备份——或者是之前说过的DoS 攻击。归纳工作负载的特征能识别这类问题,通过维护和重新配置可以解决这些问题。

如果被识别出的问题无法解决,那么可以用系统资源控制来限制它。举个例子,一个系统备份的任务压缩备份数据会消耗CPU 资源,这会影响数据库,而且还要用网络资源来传输数据。用资源控制来限定备份任务对CPU 和网络的使用(如果系统支持的话),这样虽然备份还是会发生(会慢得多),但不会影响数据库。

除了识别问题,工作负载特征归纳还可以作为输入用于仿真基准设计。如果度量工作负载只是用的均值,理想情况,你还要收集分布和变化的细节信息。这对于仿真工作负载的多样性,而不是仅测试均值负载是很重要的。关于更多的均值和变化(标准方差)的内容,参见2.8 节和第12章的内容。

工作负载分析通过辨识出负载问题,有利于将负载问题和架构问题区分开来。负载与架构在2.3 节中有过介绍。

执行工作负载特征归纳所用的工具和指标视目标而定。一部分应用程序所记录的详细的客户活动信息可以成为统计分析的数据来源。这些程序还可能提供了每日或每月的用户使用报告,这也是值得关注的。

2.5.11 向下挖掘分析

深度分析开始于在高级别检查问题,然后依据之前的发现缩小关注的范围,忽视那些无关的部分,更深入发掘那些相关的部分。整个过程会探究到软件栈较深的级别,如果需要,甚至还可以到硬件层,以求找到问题的根源。

在《Solaris 性能与工具》(Solaris Performance and Tools [McDougall 06b])一书中,针对系统性能,深度分析方法分为以下三个阶段。

1.监测:用于持续记录高层级的统计数据,如果问题出现,予以辨别和报警。

2.识别:对于给定问题,缩小研究的范围,找到可能的瓶颈。

3.分析:对特定的系统部分做进一步的检查,找到问题根源并量化问题。

监测是在公司范围内执行的,所有服务器和云数据都会聚合在一起。传统的方法是使用SNMP(简单网络管理协议),监控支持该协议的网络设备。数据可以揭示长期的变化特点,这些是无法由短时间段内运行的命令行工具获得的。如果发现怀疑的问题,多数的监测方案会报警,此时及时分析并进入下一个阶段。

问题的识别在服务器上是交互执行的,用标准的观测工具来检查系统的组件:CPU、磁盘、内存,等等。通常是用诸如vmstat(1)、iostat(1)和mpstat(1)这样的工具起一个的命令行会话来完成的。还有一些较新的工具通过GUI 支持实时的性能分析(例如,Oracle 的Oracle ZFS Storage Appliance Analytics)。

有些分析工具还具备tracing 或profiling 的功能,用以对可疑区域做更深层次的检查。做这类深层的分析可能需要定制工具乃至检查源代码(如果可以的话)。大量研究的努力就花在这里,按需要对软件栈的层次做分离来找出问题的根本原因。执行这类任务的工具包括strace(1)、truss(1)、perf 和DTrace。

五个Why

在分析阶段,你还有一个能用的方法,叫做“五个Why”技巧:问自己“why?”然后作答,以此重复五遍。下面是一个例子:

1.查询多了数据库性能就开始变差。Why?

2.由于内存换页磁盘I/O 延时。Why?

3.数据库内存用量变得太大了。Why?

4.分配器消耗的内存比应该用的多。Why?

5.分配器存在内存碎片问题。Why?

这是一个真实的例子,但出人意料的是要修复的是系统的内存分配库。是持续的质问和对问题实质的深入研究使得问题得以解决。

2.5.12 延时分析

延时分析检查完成一项操作所用的时间,然后把时间再分成小的时间段,接着对有着最大延时的时间段做再次的划分,最后定位并量化问题的根本原因。与深度分析相同,延时分析也会深入到软件栈的各层来找到延时问题的原因。

分析可以从所施加的工作负载开始,检查工作负载是如何在应用程序中处理的,然后深入到操作系统的库、系统调用、内核以及设备驱动。

举个例子,MySQL 的请求延时分析可能涉及下列问题的回答(回答的示例已经给出):

1.存在请求延时问题吗?(是的)

2.请求时间大量地花在CPU 上吗?(不在CPU 上)

3.不花在CPU 上的时间在等待什么?(文件系统I/O)

4.文件系统的I/O 时间是花在磁盘I/O 上还是锁竞争上?(磁盘I/O)

5.磁盘I/O 时间主要是随机寻址的时间还是数据传输的时间?(数据传输时间)

对于这个问题,每一步所提出的问题都将延时划分成了两个部分,然后继续分析那个较大可能的部分:延时的二分搜索法,你可以这么理解。整个过程见图2.14。

一旦识别出A 和B 中较慢的那个,就可以对其做进一步的分析和划分,依此类推。

数据库查询的延时分析是R 方法的目的。

img

图2.14 延时分析过程

2.5.13 R 方法

R 方法是针对Oracle 数据库开发的性能分析方法,意在找到延时的根源,基于Oracle 的trace events[Millsap 03]。它被描述成“基于时间的响应性能提升方法,可以得到对业务的最大经济收益”,着重于识别和量化查询过程中所消耗的时间。虽然它是用于数据库研究领域,但它的方法思想可以用于所有的系统,作为一种可能的研究手段,值得在此提及。

2.5.14 事件跟踪

系统的操作就是处理离散的事件,包括CPU 指令、磁盘I/O,以及磁盘命令、网络包、系统调用、函数库调用、应用程序事件、数据库查询,等等。性能分析通常会研究这些事件的汇总数据,诸如每秒的操作数、每秒的字节数,或者是延时的均值。有时一些重要的细节信息不会出现在这些汇总之中,因此最好的研究事件的方法是逐个检查。

网络排错常常需要逐包检查,用的工具有tcpdump(1)。下面这个例子将各个网络包归纳汇总成了一行行的文字:

img
img

tcpdump(1)按照需要可以输出各类信息(参见第10章)。

存储设备I/O 在块设备层可以用iosnoop(1M)来跟踪(DTrace-based,参见第9章):

img

这里打印出了一些时间戳,包括起始时间(STIME)、终止时间(TIME)、请求和完成之间的时间(DELTA),以及服务这次I/O 的估计时间(DTIME)。

系统调用层是另一个跟踪的常用层,工具有Linux 的strace(1)和基于Solaris 系统的truss(1)(参见第5章)。这些工具也有打印时间戳的选项。

当执行事件跟踪时,需要找到下列信息。

● 输入:事件请求的所有属性,即类型、方向、尺寸,等等。

● 时间:起始时间、终止时间、延时(差异)。

● 结果:错误状态、事件结果(大小尺寸)。

有时性能问题可以通过检查事件的属性来发现,无论是请求还是结果。事件的时间戳有利于延时分析,一般的跟踪工具都会包含这个功能。上述的tcpdump(1)用参数-ttt,输出所包含的DELTA 时间,就测量了包与包之间的时间。

研究之前发生的事件也能提供信息。一个延时特别差的事件,通常叫做延时离群值,可能是因为之前的事件而不是自身所造成的。例如,队列尾部事件的延时可能会很高,但这是由之前队列里的事件造成的,而并非该事件自身。这种情况只能用事件跟踪来加以辨别。

2.5.15 基础线统计

把当前的性能指标与之前的数值做比较,对分析问题常常有启发作用。负载和资源使用的变化是可见的,可以把问题回溯到它们刚发生的时候。某些观测工具(基于内核计数器)能显示启动以来的信息统计,可以用来与当前的活动做比较。虽然这比较粗糙,但总好过没有。另外的方法就是做基础线统计。

基础线统计包括大范围的系统观测并将数据进行保存以备将来参考。与启动以来的信息统计不同,后者会隐藏变化,基础线囊括了每秒的统计,因此变化是可见的。

在系统或应用程序变化的之前和之后都能做基础线统计,进而分析性能变化。可以不定期地执行基础线统计并把它作为站点记录的一部分,让管理员有一个参照,了解“正常”是什么样的。若是作为性能监测的一部分,可以每天都按固定间隔执行这类任务。(参见2.9 节)。

2.5.16 静态性能调整

静态性能调整处理的是架构配置的问题。其他的方法着重的是负载施加后的性能:动态性能[Elling 00]。静态性能分析是在系统空闲没有施加负载的时候执行的。

做性能分析和调整,要对系统的所有组件逐一确认下列问题:

● 该组件是需要的吗?

● 配置是针对预期的工作负载设定的吗?

● 组件的自动配置对于预期的工作负载是最优的吗?

● 有组件出现错误吗?是在降级状态(degraded state)吗?

下面是一些在静态性能调整中可能发现的问题。

● 网络接口协商:选择100Mb/s 而不是1Gb/s。

● 建立RAID 池失败。

● 使用的操作系统、应用程序或固件是旧的版本。

● 文件系统记录的尺寸和工作负载I/O 的尺寸不一致。

● 服务器意外配置了路由。

● 服务器使用的资源,诸如认证,来自远端的数据中心,而不是本地的。

幸运的是,这些问题都很容易检查。难的是要记住做这些事情!

2.5.17 缓存调优

从应用程序到磁盘,应用程序和操作系统会部署多层的缓存来提高I/O 的性能。详细内容

参见第3章的3.2.11 节。这里介绍的是各级缓存的整体调优策略:

1.缓存的大小尽量和栈的高度一样,靠近工作执行的地方,减少命中缓存的资源开销。

2.确认缓存开启并确实在工作。

3.确认缓存的命中/失效比例和失效率。

4.如果缓存的大小是动态的,确认它的当前尺寸。

5.针对工作负载调整缓存。这项工作依赖缓存的可调参数。

6.针对缓存调整工作负载。这项工作包括减少对缓存不必要的消耗,这样可以释放更多空间来给目标工作负载使用。

要小心二次缓存——比如,消耗内存的两块不同的缓存块,把相同的数据缓存了两次。

还有,要考虑到每一层缓存调优的整体性能收益。调整CPU 的L1 缓存可以节省纳秒级别的时间,当缓存失效时,用的是L2。提升CPU 的L3 缓存能避免访问速度慢得多的主存,从而获得较大的性能收益。(这些关于CPU 缓存的内容可参考第6章的内容。)

2.5.18 微基准测试

微基准测试测量的是施加了简单的人造工作负载的性能。微基准测试可以用于支持科学方法,将假设和预测放到测试中验证,或者作为容量规划的一部分来执行。

这与业界的基准定标是不同的,工业的基准定标是针对真实世界和自然的工作负载。这样的基准定标执行时需要工作负载仿真,执行和理解的复杂度高。

微基准测试由于涉及的因素较少,所以执行和理解会较为简单。可以用微基准测试工具来施加工作负载并度量性能。或者用负载生成器来产生负载,用标准的系统工具来测量性能。两种方法都可以,但最稳妥的办法是使用微基准测试工具并用标准系统工具再次确认性能数据。

下面是一些微基准测试的例子,包括一些二维测试。

● 系统调用:针对fork()、exec()、open()、read()、 close()。

● 文件系统读取:从缓存过的文件读取,读取数据大小从1B 变化到1MB。

● 网络吞吐量:针对不同的socket 缓冲区的尺寸测试TCP 端对端数据传输。

微基准测试通常在目标系统上的执行会尽可能快,测量完成大量上述这类操作所要的时间,然后计算均值(平均时间 = 运行时间/操作次数)。

后面的章节中还有微基准测试的特定方法,列出了测试的目标和属性。关于基准测试的更多内容参见第12章。