性能问题往往是复杂和神秘的,可能根本没有或很少提供关于其起源的线索。在没有起点或者没有提供方法的情况下,性能问题通常是随机分析的: 猜测问题可能在哪里,然后改变事情,直到问题消失。如果我们猜得没错的话,虽然这可能会有结果 ,但它也可能会耗费大量时间或者具有破坏性,并可能最终忽视某些问题。
关于系统性能的那些问题
典型系统中组件的数量庞大,系统的性能分析是复杂的。一个环境可以由数据库、 Web 服务器、负载均衡器和定制应用程序等组成,所有这些都运行在操作系统上(裸机或虚拟机)。这还只是软件而已,硬件和固件,包括外部的存储系统和网络基础设施,向环境中添加了更多的组件,其中任何组件都是潜在的问题根源。每一个组件都可能需要自己的专业领域,而且公司往往没有了解其环境中所有组件的员工。
性能问题还可能来自组件之间的复杂交互,这些交互可以很好地独立工作,解决这类问题可能需要多个领域的专家共同努力。
另一个复杂的因素是,性能的好坏可能是主观的: 一个用户不能接受的延迟对另一个用户可能是可以接受的。如果没有办法清楚地确定问题,不仅很难知道问题是否存在,而且很难知道问题何时得到解决。衡量性能问题的能力需要对这些问题能够量化,并根据重要性对不同问题进行排序。
性能分析方法可以提供一种有效的方法来分析系统或组件并识别问题的根本原因,而不需要深入的专业知识。方法论也可以提供识别和量化问题的方法,使它们被了解和排序。特定的性能检查表已经成为一种流行的资源。然而,可观测性仅限于列表中的特定项目,它们通常是过时且需要更新的。这些检查列表可能关注了那些可以很容易记录的已知问题,比如可调参数的设置,但不包括对源代码或环境的定制补丁。
既然如此,出现了关于性能问题的常见现象。
关于系统性能的反方法论
第一种现象是『甩锅』,常见的方式:
找到一个自己不负责的系统或环境组件。
假设问题出在那个组件上。
将问题转向负责任的团队。
如果被证明是错误的,回到第一步。
例如,“也许是网络的问题。你能不能和网络团队核实一下,看看他们是否丢失了数据包之类的东西?”这种方法不是调查性能问题,而是把它们变成别人的问题,这极可能会浪费其他团队的资源。由于缺乏数据分析,甚至从一开始就缺乏数据,导致了这一假设。
第二种方式是运行工具和收集数据,这要优于随意的假设,但它仍不足以进行有效的性能分析,这是缺乏深思熟虑的方法。用户通过选择熟悉的、在互联网上发现的或随机发现的可观测性工具来分析性能,然后看看是否有任何明显的迹象出现。这种随机应变的方法可能会忽略许多类型的问题。
找到合适的工具需要一段时间,最熟悉的工具会首先运行,即使它们不是最有意义的。学习更多的工具有所帮助,但仍然是一个有限的方法。由于缺乏可观察性工具或指标,某些系统组件或资源可能会被忽略。此外,我们可能没有意识到视图是不完整的,没有办法识别“未知的未知”。
现有的性能分析方法
可以使用更好的性能分析方法,在运行工具之前可以解决问题,包括问题陈述方法、负载塑造法和钻取分析法。
问题陈述法
客服或者支持人员收集问题信息时通常使用问题陈述法,可以用于性能分析。其目的是收集问题的详细描述以指导更深入的分析,这个描述本身甚至可以解决这个问题。这通常是通过询问以下问题进入分析环节:
什么会被认为存在性能问题?
这个系统曾经运行良好吗?
最近系统有什么变化? (软件? 硬件? 负载?)
性能下降是否可以用延迟或执行时间来表示?
这个问题是否影响到其他人或应用程序(或者只是影响到自己) ?
环境是什么? 使用什么软件和硬件? 版本? 配置?
这些问题可以根据具体场景进行定制。这样的问题看起来很明显,答案往往也能解决了一类问题,不需要更深入的方法。当情况不是这样时,可以调用其他方法来提供服务,包括负载塑造法和钻取分析法。
负载塑造法
可以是有属性的,例如:
负载是谁引起的? 进程 ID、用户 ID、远程 IP 地址?
为什么要产生异常负载? 可能的代码路径?
负载的其他特征是什么? IO、吞吐量、类型?
负载是如何随着时间变化的?
这些问题有助于将负载问题与架构问题分离开来。最好的性能往往来自于消除不必要的工作。有时,这些瓶颈是由应用程序故障(例如,线程卡在循环中)造成的,也可能是由于错误配置产生的。通过维护或重新配置,这些不必要的工作可以被消除,负载塑造可以识别这类变更问题。
钻取分析法
钻取分析包括剥离软件和硬件的层次,以找到问题的核心,从高到低深入到不同层次的细节。这些更深入的细节甚至包括检查内核的内部构造,例如,通过对内核堆栈跟踪进行取样分析,或者通过动态跟踪检查内核函数的执行。
钻取分析法包括:
监测,不断地记录许多系统随时间变化的高级统计数据,如果出现问题,则识别或发出警报。
识别,如果系统存在疑似问题,这将调查范围缩小到使用系统工具和识别可能瓶颈的特定资源或感兴趣的领域。
分析,进一步审查特定的系统领域,找出根本原因,并将问题量化。
分析阶段可以向下钻取,从软件堆栈顶部的应用程序开始,向下钻取到系统库、系统调用、内核内部、设备驱动程序和硬件。虽然向下钻取分析常常能够确定问题的根本原因,但是这样做可能会耗费时间,而且当向错误的方向钻取时,可能会浪费大量的时间。
有没有更高效的方法么?
关于性能问题的早期快速定位
对于每个资源,我们可以检查它的利用率、饱和度和错误。资源意味着单独检查的所有物理服务器功能组件(cpu、磁盘、总线等),一些软件资源也可以使用相同的方法进行检查。
利用率是资源在特定时间间隔内工作时间的百分比。在忙时,资源可能仍然能够接受更多的负载, 具体与否可以通过饱和度来确定。对于某些资源类型,例如内存,利用率是所使用资源的容量,这与基于时间的定义不同。一旦容量资源达到100% 的利用率,要么将负载(饱和度)排入队列,要么返回错误。错误是指错误事件的数量,当故障模式是可恢复的时候,它们可能不会立即被注意到。这包括失败和重试的操作,以及在冗余设备池中失败的设备。
尝试迭代系统资源,而不是从工具开始,创建一个要问的问题完整列表,只搜索用于回答这些问题的工具。即使找不到回答这些问题的工具,也是极其有用的,它们现在变成“已知的未知”。
尝试指向有限数量的关键指标,以便尽可能快地检查所有系统资源。在此之后,如果没有发现问题,可以转向其他方法。
度量指标
度量指标包括:
利用率:一个时间间隔内的百分比(例如,一个 CPU 的利用率为90%)。
饱和度:等待队列长度(例如,CPU的平均运行队列长度为4)。
错误报告中的错误数量(例如,最后50次网络交互的冲突)。
表示测量的时间间隔也很重要。虽然看起来有些违反直觉,即使在较长的时间间隔内总利用率很低,但短时间的高利用率会导致性能问题。例如,CPU 利用率在秒级可能有很大的差异,五分钟内的平均利用率会掩盖达到100% 的短时间段内的饱和度问题。
资源列表
快速定位性能问题需要一个完整的资源列表,例如,一个服务器硬件资源的通用列表如下:
CPU ー sockets、核心、硬件线程(虚拟 cpu)。
内存ー DRAM
网络接口ー以太网口
储存ー磁盘
控制器ー存储、网络。
每个组件通常是单个资源类型。例如,主存是容量资源,网络接口是 I/O 资源,可以用 IOPS 或吞吐量来度量。有些组件可以表现为多种资源类型ー例如,存储设备既是 I/O 资源又是容量资源,需要考虑所有可能导致性能瓶颈的类型。需要注意的是,I/O 资源可以进一步抽象为排队系统,对这些请求进行排队,然后为其提供服务。
高利用率或高饱和度下导致性能瓶颈的资源是最值得关注, 而缓存在高利用率下提高性能。在排除系统瓶颈之后,可以检查缓存命中率和其他性能属性。如果不能确定是否要包含一个资源,就包含它,然后看看这个度量指标在实践中工作得如何。
功能模块图
一种遍历资源的方法是查找或绘制系统的功能模块图。这种类型的图表显示了资源之间的关系,这在寻找数据流中的瓶颈时非常有用。在确定各种总线的利用率时,在功能图上用其最大带宽标注每个总线。在进行单一测量之前,可以根据功能模块图来探查系统瓶颈。
CPU、内存和 I/O 的互连接性常常被忽略。幸运的是,它们通常不是导致系统瓶颈的原因。不幸的是,当它们出现时,问题可能很难解决,或许需要可以升级主板或减少负载。
度量方法
一旦获得了资源列表,需要考虑每个资源所需的度量类型(利用率、饱和度和错误数量)。这些指标可以表示为每个时间间隔的平均值或者计数。
注意当前那些不可用的指标:,这些“已知的未知”最终将得到一个包含大约几十个指标的列表,其中有些很难度量,有些则根本无法度量。幸运的是,最常见的问题通常出现在更简单的指标上,例如,CPU 饱和度、内存容量饱和度、网络接口利用率、磁盘利用率等 ,因此可以先检查这些指标。
其中一些指标可能无法从操作系统工具中获得,必须使用DTrace或 CPU 性能工具编写自己的软件来获得这些指标。
软件资源
一些软件资源也可以进行类似的检查,通常适用于较小的软件组件,而不适用于整个应用程序。例如:
互斥锁。利用率可以定义为锁被持有的时间,饱和度是锁等待的线程排队。
线程池。利用率可以定义为线程忙于处理工作的时间,饱和度可以定义为等待线程池处理的请求数量。
进程/线程容量。系统可能有数量受限的进程或线程,其当前使用可定义为利用率; 等待分配可能表明饱和度; 当分配失败时发生错误。
文件描述符容量,与上面的类似。
如果可以正常度量,就可以使用这些指标; 否则,软件故障排除可以留给其他方法。
性能定位的策略
性能问题定位的核心是确定使用哪些度量指标,从操作系统读取这些指标后,需要解释这些指标当前的值。对于某些度量指标,解释可能是显而易见的,并且有很好的文档记)。如果不明显的指标,可能取决于工作量需求或对他们的预期。错误排在第一位,因为它们通常比利用率和饱和度更容易、更快地进行解释。
不幸的是,一个系统可能会遇到不止一个性能问题,首先要完成标准动作, 检查资源表并列出所有发现的问题,然后根据可能的优先级对每个问题进行调查会更有效。
小结
系统性能分析可能是复杂的,任何组件都可能产生问题,包括它们之间的交互。常用的方法有时类似于猜测,尝试熟悉的工具或者在没有确凿证据的情况下提出假设。我们可以执行一种简单策略,对完整的系统进行健康检查。考虑所有的资源,以避免忽略问题,使用有限的度量,以便能够迅速遵循,这对于分布式环境(包括云计算)尤其重要。