性能学习01:零基础进入性能领域

笔者:风起怨江南 出处:https://blog.****.net/JackMengJin 笔者原创,文章转载需注明,如果喜欢请点赞+关注,感谢支持!

导读:让学习成为习惯,从性能零基础小白一步步晋升为性能大牛!最后祝愿大家1024程序员节日快乐!

资料:《性能之巅:洞悉系统、企业与云计算》,格雷格(Gregg,B.)著

 

目录

零基础进入性能领域

1.系统性能

2.人员、事情、视角

3.性能测试

4.延时

5.动态跟踪

6.云计算

7.案例研究

7.1 缓慢的磁盘

7.2 软件变更


 

零基础进入性能领域

从系统性能、人员、事情、视角、性能测试、延时、动态跟踪、云计算、案例研究几个方面简单的讲解,逐步开始性能领域的学习,通过性能学习系列可以学习到分析和提高系统与应用程序性能的方法,最终实现复杂的环境的性能测试、性能分析与调优。

 

1.系统性能

系统性能是对整个系统的研究,包括了所有的硬件组件和整个软件栈。所有数据路径上和软硬件上所发生的事情都包括在内,因为这些都有可能影响性能。对于分布式系统来说,这意味着多台服务器和多个应用。

性能学习01:零基础进入性能领域

上图呈现的是单台服务器上的通用系统软件栈,包括操作系统(OS)内核、数据库和应用程序层

术语“全栈”(entire stack)有时一般仅仅指的是应用程序环境,包括数据库、应用程序,以及网站服务器。不过,当论及系统性能时,我们用全栈来表示所有事情,包括系统库和内核。

 

2.人员、事情、视角

人员:系统性能是一项需要多类人员参与的事务,其中包括系统管理员技术支持人员应用开发者数据库管理员网络管理员。在性能领域也有某些专职于特定应用程序的工种,例如,Java性能工程师MySQL 性能工程师

事情:性能领域包括了以下的事情,按照理想的执行顺序将它们排列如下:

  1. 设置性能目标和建立性能模型
  2. 基于软件或硬件原型进行性能特征归纳
  3. 对开发代码进行性能分析(软件整合之前)
  4. 执行软件非回归性测试(软件发布前或发布后)
  5. 针对软件发布版本的基准测试
  6. 目标环境中的概念验证(Proof-of-concept)测试
  7. 生产环境部署的配置优化
  8. 监控生产环境中运行的软件
  9. 特定问题的性能分析

步骤1~5 是传统软件产品开发过程的一部分。产品发行之后,接下来要么是在客户环境中进行概念验证测试,要么直接进行部署和配置。如果在客户环境中碰到问题(步骤6~9),这说明该问题在软件开发阶段没有得到发现和修复。

理想情况下,在硬件选型和软件开发之前,性能工程师就应该开始工作:

作为工作的第一步,可以设定性能目标并建立一个性能模型。产品开发过程常常缺失了这一步,性能工程工作被推迟直到问题出现。在架构决策确定之后,随着软件开发工作的一步步推进,修复性能问题的难度会变得越来越大。术语容量规划(capacity planning)指的是一系列事前行动。在设计阶段,包括通过研究开发软件的资源占用情况,来得知原有设计在多大程度上能满足目标需求。在部署后,包括监控资源的使用情况,这样问题在出现之前就能被预测。能够有助于完成上述事情的方法和工具在本书中都有覆盖。

视角:与很多事情专注于一点不同,性能是可以从不同的视角来审视的。下图展示了两种性能分析的视角:

负载分析(workload analysis)资源分析(resource analysis),二者从不同的方向对软件栈做分析。

性能学习01:零基础进入性能领域

分析视角系统管理员作为系统资源的负责人,通常采用资源分析视角

应用程序开发人员,对最终实现的负载性能负责,通常采用负载分析视角

 

3.性能测试

性能测试是充满挑战的系统性能工程是一个充满挑战的领域,具体原因有很多,其中包括以下事实,系统性能是主观的、复杂的,而且常常是多问题并存的。性能是主观的技术学科往往是客观的,太多的业界人士审视问题非黑即白。

在进行软件故障查找的时候,判断bug 是否存在或bug 是否修复就是这样。bug 的出现总是伴随着错误信息,错误信息通常容易解读,进而你就明白错误为什么会出现了。与此不同,性能常常是主观性的。开始着手性能问题的时候,对问题是否存在的判断都有可能是模糊的,在问题被修复的时候也同样,被一个用户认为是“不好”的性能,另一个用户可能认为是“好”的。

试想一个问题:磁盘的平均I/O 响应时间是1ms。这是“好”还是“坏”?响应时间或者说是延时,虽然作为最好的衡量指标之一,但还是难以用来说明延时的情况。从某种程度上说,一个给定指标是“好”或“坏”取决于应用开发人员和最终用户的性能预期。通过定义清晰的目标,诸如目标平均响应时间,或者对落进一定响应延时范围内的请求统计其百分比,可以把主观的性能变得客观化。

系统是复杂的除了主观性之外,性能工程作为一门充满了挑战的学科,除了因为系统的复杂性,还因为对于性能,我们常常缺少一个明确的分析起点。有时我们只是从猜测开始,比如,责怪网络,而性能分析必须对这是不是一个正确的方向做出判断。性能问题可能出在子系统之间复杂的互联上,即便这些子系统隔离时表现得都很好。也可能由于连锁故障(cascadingfailure)出现性能问题,这指的是一个出现故障的组件会导致其他组件产生性能问题。要理解这些产生的问题,你必须理清组件之间的关系,还要了解它们是怎样协作的。

瓶颈往往是复杂的,还会以意想不到的方式互相联系。修复了一个问题可能只是把瓶颈推向了系统里的其他地方,导致系统的整体性能并没有得到期望的提升。除了系统的复杂性之外,生产环境负载的复杂特性也可能会导致性能问题。在实验室环境很难重现这类情况,或者只能间歇式地重现。解决复杂的性能问题常常需要全局性的方法。整个系统——包括自身内部和外部的交互——都可能需要被调查研究。这项工作要求有非常广泛的技能,一般不太可能集中在一人身上,这促使性能工程成为一门多变的并且充满智力挑战的工作。

可能有多个问题并存找到一个性能问题点往往并不是问题本身,在复杂的软件中通常会有多个问题。为了证明这一点,试着找到你的操作系统或应用程序的bug 数据库。一般情况下,成熟的软件,即便是那些被认为拥有高性能的软件,也会有不少已知的但仍未被修复的性能问题。这就造成了性能分析的又一个难点:真正的任务不是寻找问题,而是辨别问题或者说是辨别哪些问题是最重要的。

要做到这一点,性能分析必须量化(quantify)问题的重要程度。某些性能问题可能并不适用于你的工作负载或者只在非常小的程度上适用。理想情况下,你不仅要量化问题,还要估计每个问题修复后能带来的增速。

有一个指标非常适合用来量化性能,那就是延时(latency)。

 

4.延时

延时测量的是用于等待的时间。

广义来说,它可以表示所有操作完成的耗时,例如,一次应用程序请求、一次数据库查询、一次文件系统操作等。

延时可以表示从点击链接到屏幕显示整个网页加载完成的时间。作为一个指标,延时可以估计最大增速(maximumspeedup)。

下图显示了一次数据库查询需要100ms 的时间(这就是延时),其中80ms 的阻塞是等待磁盘读取。通过减少磁盘读取时间(如通过使用缓存)可以达到最好的性能提升,并且可以计算出结果是五倍速(5x)。这就是估计出的增速,而且该计算还可以对性能问题做量化:是磁盘读取让查询慢了5 倍。

性能学习01:零基础进入性能领域

这样的计算对其他的指标类型不一定适用。比如,每秒发生的I/O 操作次数(IOPS),取决于I/O 的类型,往往不具备直接的可比性。如果一个变化导致IOPS 下降了80%,也很难知道这带来的性能影响会是怎样的。有可能是IOPS 小了5 倍,但若所有这些I/O 的数据量(字节)都变大10 倍了呢?

若以网络作为讨论背景,延时指的是建立一个连接的时间,而不是数据传输的时间。虽然延时是一个非常有用的指标,但也不是随时随地都能获得。某些系统只有平均延时,某些系统则完全没有延时指标。动态跟踪(dynamic tracing)可以从任意感兴趣的点测量延时,还可以提供数据以显示延时完整的分布情况。

 

5.动态跟踪

动态跟踪技术把所有的软件变得可以监控,而且能用在真实的生产环境中。

这项技术利用内存中的CPU 指令并在这些指令之上动态构建监测数据。这样从任何运行的软件中都可以获得定制化的性能统计数据,从而提供了远超系统的自带统计所能给予的观测性。从前因为不易观测而无法处理的问题变得可以解决。从前可以解决而难以解决的问题,现在也往往可以得以简化。动态跟踪与传统的观测方法相比是如此不同,甚至让人很难一开始就抓住动态跟踪的要领。

以操作系统内核举例:分析内核好比闯进了一间黑屋,拿着蜡烛(系统自带统计)去照亮内核工程师他们觉得需要照亮的地方,而动态跟踪则像是手电筒,你可以指哪儿亮哪儿。

 

6.云计算

给系统性能带来影响的最新进展来自云计算和云计算的根基——虚拟技术的兴起。

云计算采用的架构能让应用程序均衡分布于数目不断增多的小型系统中,这让快速扩展成为可能。这种方法还降低了对容量规划的精确程度的要求,因为更多的容量可以很便捷地在云端添加。在某些情况下,它对性能分析的需求更高了:使用较少的资源就意味着系统更少。云的使用通常是按小时计费的,性能的优势可以减少系统的使用数目,从而直接节约成本。这和企业用户的情况不同,企业用户被一个支持协议锁定数年,直到合同终结都可能无法实现成本的节约。云计算和虚拟化技术也带来了新的难题,这包括,如何管理其他租户(tenant,有时被称作性能隔离(performanceisolation))带来的性能影响,以及如何让每个租户都能对物理系统做观测。举个例子,除非系统管理得很好,否则磁盘I/O 性能可能因为同邻近租户的竞争而下降。在某些环境中,并不是每一个租户都能观察到物理磁盘的真实使用情况,这让问题的甄别变得困难。

 

7.案例研究

案例研究会讲述什么时候该做什么事和为什么要做这些事,把这些关联到当前的环境会有很大的帮助。接下来是两个虚构的示例;一个是与磁盘I/O 相关的性能问题,另一个是软件新版本的性能测试。

 

7.1 缓慢的磁盘

Scott 是一家中型公司里的系统管理员。数据库团队报告了一个支持ticket(工单),抱怨他们有一台数据库服务器“磁盘缓慢”。

Scott 首要的任务是多了解问题的情况,收集信息形成完整的问题陈述。ticket 中抱怨磁盘慢,但是并没解释这是否是由数据库引发的。Scott 的回复问了以下这些问题:

  1. 当前是否存在数据库性能问题?如何度量它?
  2. 问题出现至今多长时间了?
  3. 最近数据库有任何变动吗?
  4. 为什么怀疑是磁盘?

数据库团队回复:“我们的日志显示有查询的延时超过了1000ms,这并不常见,就在过去的一周这类查询的数目达到了每小时几十个。AcmeMon 显示磁盘在那段时间很繁忙。”可以肯定确实存在数据库的问题,但是也可以看出关于磁盘的问题更多的是一种猜测。Scott需要检查磁盘,同时他也要快速地检查一下其他资源,以免这个猜测是错误的。

AcmeMon 是公司服务器的基础监控系统,基于mpstat(1)、iostat(1)等其他的系统工具,提供性能的历史图表。Scott 登录到AcmeMon 上自己查看问题。一开始,Scott 使用了一种叫做USE 的方法来快速检查系统瓶颈。正如数据库团队所报告的一样,磁盘的使用率很高,在80%左右,同时其他资源(CPU、网络)的使用率却低得多。

历史数据显示磁盘的使用率在过去的一周内稳步上升,而CPU的使用率则持平。AcmeMon 不提供磁盘饱和(或错误)的统计数据,所以为了使用USE 方法,Scott 必须登录到服务器上并运行几条命令。他在/proc 目录里检查磁盘错误数,显示是零。他以一秒钟作为间隔运行iostat,对使用率和饱和率观察了一段时间。AcmeMon 报告80%的使用率是以一分钟作为间隔的。在一秒钟的粒度下,Scott 看到磁盘使用率在波动,并且常常达到100%,造成了饱和,加大了磁盘I/O 的延时。

为了进一步确定这是阻塞数据库的原因——延时相对于数据库的查询不是异步的——他利用动态跟踪脚本来捕捉时间戳和每次数据库被内核取消调度时数据库的栈跟踪。他发现数据库在查询过程中常常被一个文件系统读操作阻塞,一阻塞就是好几毫秒。对于Scott 来说,这些证据已经足够了。接下来的问题是为什么。磁盘性能统计显示负载持续很高。

Scott 对负载进行了特征归纳以便做更多了解,使用iostat(1)来测量IOPS、吞吐量、平均磁盘I/O 延时和读写比。从这些结果,他计算出了平均I/O 的大小并对访问模式做了估计:随机或者连续。Scott 可以通过I/O级别的跟踪来获得更多的信息,然而,他觉得这些已经足够表明这个问题是一个磁盘高负载的情况,而非磁盘本身的问题。Scott 在ticket 中添加了更多的信息,陈述了自己检查的内容并上传了检查磁盘所用到的命令截屏。他目前总结的结果是由于磁盘处于高负载状态,从而使得I/O 延时增加,进而延缓了查询。但是,这些磁盘看起来对于这些负载工作得很正常。因此他问道,难道有一个更简单的解释:数据库的负载增加了?

数据库团队的回答是没有,并且数据库查询率(AcmeMon并没有显示这个)始终是持平的。这看起来和最初的发现是一致的,CPU 的使用率也是持平的。Scott 思考着还会有什么因素会导致磁盘的高I/O 负载而又不引起CPU 可见的使用率提升,他和同事简单讨论了一下这个问题。

一个同事推测可能是文件系统碎片,碎片预计会在文件系统空间使用接近100%时出现。Scott 查了一下发现,磁盘空间使用率仅仅为30%。Scott 知道他可以进行更为深入的分析来了解磁盘I/O 问题的根源,但这样做太耗时。基于自己对内核I/O 栈的了解,他试图想出其他简单的分析,以此来做快速的检查。他想到这次的磁盘I/O 是由文件系统缓存(页缓存)未命中导致的。Scott 进而检查了文件系统缓存的命中率,发现当前是91%。这看起来还是很高的(很好),但是他没有历史数据可与之比较。他登录到其他有相似工作负载的数据库服务器上,发现它们的缓存命中率超过了97%。他同时发现问题服务器上的文件系统缓存大小要比其他服务器大得多。

于是他把注意力转移到了文件系统缓存大小和服务器内存使用情况上,发现了一些之前忽视的事情:一个开发项目的原型应用程序不断地消耗内存,虽然它并不处于生产负载之下。这些被占用的内存原本可以用作文件系统缓存,这使得缓存命中率降低,让磁盘I/O 负载升高,损害了生产数据库服务器的性能。Scott 联系了应用程序开发团队,让他们关闭该应用程序,并将其放到另一台服务器上,作为数据库问题的参照。随后在AcmeMon 上Scott 看到了磁盘使用率的缓慢下降,同时文件系统缓存恢复到了它原先的水平。被拖慢的数据库查询数目变成了零,他关闭了ticket 并将它置为“已解决”。

 

7.2 软件变更

Pamela 在一家小公司做性能扩展工程师,负责所有与性能相关的事务。应用程序开发人员开发了一个新的核心功能,但是他们不确定引入这个功能会不会影响性能。在部署到生产环境之前,Pamela 决定对这个应用程序的新版本执行一次非回归性测试。(非回归性测试是用来确认软件或硬件的变更并没有让性能倒退的。)

为了这个测试,Pamela 需要一台空闲的服务器和一台客户负载的模拟器。应用程序团队之前写过一个模拟器,虽然这个模拟器还有诸多限制和一些已知的bug,她还是决定一试,但要确定它能够充分地模拟生产环境的工作负载。她依照生产环境的配置设置好服务器,从另一个系统向目标开启客户端工作负载模拟器。客户工作负载可以通过研究访问日志来进行分析,不过公司里已经有一个工具在做这件事情,她直接就用了。她还用这个工具来分析同一天不同时间段的生产环境日志,这样来对两个工作负载进行比较。她发现客户负载模拟器虽然可以提供一般性的生产环境工作负载,但是对负载的多样性无能为力。Pamela 记下了这一点后继续她的分析。

这时,Pamela 知道有很多方法可以用。她选择了最简单的那个:增加客户模拟器的负载直至达到一个极限。客户负载模拟器可以设定每秒钟执行的客户数目,其默认值是1000,她之前使用的就是这个值。Pamela 决定从100 客户请求开始,以每次100 为增量逐步增加负载,直至达到极限,每一个测试级别都测试一分钟。她写了一个shell 脚本来执行这个测试,将结果收集到一个文件里供其他工具绘图。随着负载不断增加,她通过执行动态基准测试来判定限制因素。服务器资源和服务器的线程看起来有大量空闲。客户模拟器显示完成的请求数稳定在大约每秒700 客户请求。她切换到了新的软件版本并重复相同的测试。这次也是到了700 客户请求就稳定不动了。她分析了服务器,试图寻找限制的原因,但是一无所获。她把结果绘成图,画出了请求完成率相对于负载的变化情况,以此来观察不同软件版本的扩展特性。新旧两个软件版本都有一个很突兀的上限。

虽然看起来两个软件版本所拥有的性能特性是相似的,但Pamela 还是很失望,因为她找不到是什么因素制约客户数的扩展。她知道她检查的只有服务器资源,限制的原因可能出在应用程序的逻辑上,也可能是其他地方:网络或者客户模拟器上。Pamela 想知道是不是需要采取一种不同的方法,例如,跑一个固定量的操作,然后记录资源使用的汇总情况(CPU、磁盘I/O、网络I/O),这样就可以表示出单一客户请求的资源使用量。她针对当前的和新的软件版本,按照每秒700 客户的请求量来运行客户模拟器,并测量了资源的消耗情况。当前的软件版本对于给定负载,跑在32 个CPU 上的使用率达到了20%。新的软件版本对于同样的负载,在相同CPU 数目上则是30%的使用率。看得出这确实是一个性能倒退,占用了更多的CPU资源。

为了理解700 的上限,Pamela 运行了一个更高的负载并研究了在数据路径上的所有组件,包括网络、客户系统和客户工作负载产生器。她还对服务端和客户端软件做了向下挖掘分析。她把所做的检查都做了记录,包括屏幕截图,以作为参考。为了研究客户端软件,她执行了线程状态分析,发现这是一个单线程的软件。单线程100%的执行时间都花在了一个CPU上。这使得她确认这就是测试的限制因素所在。作为验证实验,她在不同客户系统上并行运行客户端软件。用这种方式,无论当前版本软件还是新版本的软件,她都让服务器达到了100%的CPU 使用率。这样,当前版本达到了每秒3500 请求,新版本则是每秒2300 请求,这与之前资源消耗的发现是一致的。Pamela 通知应用软件开发人员新的软件版本有性能倒退,她打算对CPU 的使用做剖析来查找原因:看看是哪条代码路径导致的。她强调指出一般性的生产工作负载已被测试过了,但多样性的工作负载还未曾测过。她还发了一个bug,说明客户工作负载生成器是单线程的,这是会成为瓶颈的。

 


欢迎进入性能学习的世界,以上便是《性能学习01:零基础进入性能领域》的所有内容。如果喜欢请点赞和关注,谢谢支持!

安利这本作者格雷格(Gregg,B.)编写的《性能之巅:洞悉系统、企业与云计算》,希望可以在性能学习上对大家有所帮助。