mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2025-12-28 19:16:02 +08:00
mod
This commit is contained in:
118
极客时间专栏/性能工程高手课/性能分析/14 | 性能分析概述:性能问题归根结底是什么原因?.md
Normal file
118
极客时间专栏/性能工程高手课/性能分析/14 | 性能分析概述:性能问题归根结底是什么原因?.md
Normal file
@@ -0,0 +1,118 @@
|
||||
<audio id="audio" title="14 | 性能分析概述:性能问题归根结底是什么原因?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/75/b2/75ec3d7a277b43ec2c43e8a6388e85b2.mp3"></audio>
|
||||
|
||||
你好,我是庄振运。
|
||||
|
||||
前面几讲,我们讨论了有关性能测试的内容,了解了各种测试的种类、测试的规划、测试的工具以及执行测试的经验教训。在整个性能优化的世界里,性能测试是基础,在这一基础上,我们才能对各种性能问题进行性能分析,并进行相应的性能优化。
|
||||
|
||||
从这一讲开始,我们来探讨一下常见的各种性能问题,和如何进行相应的性能分析。我们首先宏观地介绍一下性能的外部指标、内部瓶颈和资源制约,以及如何正确地进行性能分析。然后接下来的几讲分别重点讨论CPU、内存、存储和网络几个领域。
|
||||
|
||||
## 性能的外部指标
|
||||
|
||||
谈论应用程序系统和互联网服务的性能时,我们首先要清楚外部的性能指标是什么。最重要也最普遍的指标有三个:服务延迟(Service Latency)、吞吐率(Throughput)和资源使用率(Resource Utilization)。我们分别来看看。
|
||||
|
||||
### 服务延迟
|
||||
|
||||
**服务延迟**(或者叫访问延迟),指的是客户发出的请求被成功服务的时间。
|
||||
|
||||
虽然具体的定义和度量有很多种,比如有些度量只考虑系统本身的服务时间,而不考虑其它因素(比如网络传输时间)。但我个人坚持,这个指标是直接针对客户体验的,因此不能仅仅从自己的系统角度衡量,而必须是端到端的延迟度量(End To End, or E2E)。因为只有这样来定义这一指标,才能准确地抓住“外部性能”这一特点。
|
||||
|
||||
任何系统和服务的设计和搭建都是为了给客户来用的,如果不紧紧抓住“客户体验”这一根本目标,性能测试、性能分析,以至于性能优化也就成了无的放矢,基本失去了意义。
|
||||
|
||||
### 吞吐率
|
||||
|
||||
**吞吐率**指的是单位时间(比如每秒钟)可以成功处理的请求数或任务数。
|
||||
|
||||
这一指标和前面讲的访问延迟指标相辅相成,一个注重时间,就是服务延迟;一个注重空间,也就是系统容量。一个系统的外部性能主要受到这两个条件的约束,缺一不可。
|
||||
|
||||
比如,一个在线聊天服务系统,可以提供每秒钟一百万的吞吐率,但是客户的访问延迟是5分钟以上,那么这个“一百万的吞吐率”没啥意义。反之,访问延迟很短,但是吞吐率很低,同样没有意义。
|
||||
|
||||
所以,一个系统的性能必然受到这两个条件的同时作用。
|
||||
|
||||
### 资源使用率
|
||||
|
||||
一个系统和服务总是需要软硬件容量来支撑的,那么**资源的使用率**就至关重要了。因为它直接决定了系统和服务的运营成本。
|
||||
|
||||
这一指标虽然主要是面向系统容量的,但其实和客户也直接相关。如果资源使用率低,比如CPU使用率低,在系统容量固定(比如服务器数目固定)的情况下,吞吐率也会较低,或者访问延迟会较高,因为系统资源没有被充分利用。
|
||||
|
||||
## 外部性能指标的变化
|
||||
|
||||
我们还需要知道,这三个性能指标的变化有它们自己的特点,而且经常会互相影响。
|
||||
|
||||
对一个系统而言,如果吞吐率很低,**服务延迟**往往会非常稳定。当吞吐率增高时,访问延迟一般会快速增加。
|
||||
|
||||
下图展示了一个有代表性的系统的吞吐率和访问延迟的变化。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/07/d8/0784f05afc0c2a37205e1b0b5827aad8.png" alt="">
|
||||
|
||||
在吞吐率低于每秒600时,访问延迟小于5毫秒,这个延迟相对稳定。然后随着吞吐率变大,访问延迟飞速攀升。一般而言,根据系统的延迟可接受大小,我们需要控制负载流量,以免访问延迟过大而影响客户体验。
|
||||
|
||||
我们在测量访问延迟的时候,不仅要计算均值,还需要注意延迟的分布情况,比如,有百分之几的在服务允许的范围,有百分之几的略微超出了,有百分之几的完全不可接受。多数情况下,平均延迟达标了,但是其中可能有很大比例(比如20%)远远超出了我们可接受的范围。
|
||||
|
||||
所以,我们在规定延迟标准的时候,除了均值,还需要定义百分位数的可接受值。比如,平均延迟10毫秒,P90小于30毫秒,P99小于50毫秒等等。
|
||||
|
||||
关于**吞吐率**,现实中的系统往往有一个峰值极限。
|
||||
|
||||
超过这个峰值极限,系统就会超载,除了服务延迟超标,还会造成一系列的性能问题(比如系统挂掉)。这个峰值极限往往需要经过仔细的性能测试,并且结合访问延迟标准来确定。有了这个峰值极限值后,系统的设计和运维就需要确保系统的负载不要超过这个值。
|
||||
|
||||
除了影响运营成本和系统容量,**资源使用率**的标准也需要考虑其他几个重要因素。
|
||||
|
||||
一个因素是意外事件的缓冲(Buffer)和灾难恢复(Disaster Recovery, or DR)。一个现实世界中的系统,随时都会有意外事件(比如流量波动)或者部分网络故障,这就需要整个系统资源保留一定的缓冲,来应付这些意外和从发生的灾难中恢复。比如CPU的使用率,虽然理论上可以到100%,但考虑这些因素,实际的使用率指标往往远远低于100%。
|
||||
|
||||
## 性能问题归根结底是某个资源不够
|
||||
|
||||
所有的性能问题,虽然表现方式各异,归根结底都是某种资源受到制约,不够用了。这里的资源指的是一个计算机系统,程序和互联网服务会用到的每一种资源,比如CPU、网络等。换句话说,客户的请求在处理时在某个地方“卡住了”。这个卡住的地方就叫“瓶颈”(或者叫卡点,Choke point)。
|
||||
|
||||
根据我的经验,我在下面这张图表中展示了一个系统常见的十大瓶颈,基本上覆盖了所有可能出现性能问题的地方。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/ba/d8/ba67d606c4d8246075779100502308d8.png" alt="">
|
||||
|
||||
这十大瓶颈可以大致分为四类,在后面的几讲中我们会详细讨论分析每一类别里面的具体性能问题。这四类是:
|
||||
|
||||
1. 软件系统:包括操作系统、应用程序、各种类库以及文件系统。
|
||||
1. CPU和内存:包括CPU性能、QPI(QuickPath Interconnect,处理器之间的快速通道互联)和缓存内存。
|
||||
1. 存储和外部IO:包括处理器的IO的接口性能、各种存储系统(尤其是HDD和SSD性能)。
|
||||
1. 网络:包括服务器到机柜交换机的网络、数据中心的网络、CDN和互联网。
|
||||
|
||||
总体上来讲,性能分析的目的,是提供高性能、低延迟、高效率的服务。
|
||||
|
||||
要实现这一目的,就需要找到系统和服务的性能瓶颈,然后尽可能的消除瓶颈,或者降低瓶颈带来的影响。系统和服务有性能瓶颈就说明这个地方的资源不够用了。所谓最大的性能瓶颈,就是说这个地方的资源短缺程度最大,相对而言,其他地方的资源有富余。
|
||||
|
||||
如何找到最大的性能瓶颈?
|
||||
|
||||
这就需要进行性能测试和性能分析了。性能分析时需要知道三个层次的知识:
|
||||
|
||||
第一个层次是可能的性能瓶颈,比如我们刚刚讨论的十大瓶颈。知道了瓶颈才能有目标的去分析。
|
||||
|
||||
第二个层次是每个瓶颈有哪些资源有可能短缺。比如内存就有很多种不同的资源,不仅仅是简单的内存大小。除了内存使用量,还有内存带宽和内存访问延迟。
|
||||
|
||||
第三个层次是对每个瓶颈的每种资源要了解它和其他模块是如何交互的,对整个系统性能是如何影响的,它的正常值和极限值是多少,如何分析测量等等。
|
||||
|
||||
找到性能最大瓶颈后,具体的优化方式就是什么资源不够就加什么资源,同时尽量降低资源消耗,这样就可以做到在资源总量一定的情况下,有能力支撑更高的吞吐率和实现更低的延迟。
|
||||
|
||||
## 依据数据和剖析(Profiling)来分析
|
||||
|
||||
做性能分析时,必须采用科学的方法,尽量依据数据,来引导我们的分析和验证我们的推论,而不是完全凭空猜测。
|
||||
|
||||
当我们有了比较多的性能分析和优化的经验后,慢慢就会对一个系统的内部各个模块的交互,以及各种性能问题肚里有数了。这种时候,适度地做一些理论推测是合理的。就像一个有经验的医生,往往稍微了解一下病人的情况,就猜个八九不离十。这就是经验的重要性。
|
||||
|
||||
不过,再有经验的医生,还是需要做进一步的检验,尤其是面对复杂的病人和病情。同样的,无论性能分析的经验多丰富,我们也需要谨慎地做性能测试和数据分析,尤其是在针对重要系统的时候。
|
||||
|
||||
这一点可以说是性能分析和优化的第一原则。当我们怀疑性能有问题的时候,应该通过合理的测试、日志分析,并作合适的剖析(Profillig),来分析出哪里有问题,从而有的放矢,而不是凭感觉、撞运气。
|
||||
|
||||
比如,如果是CPU相关的性能问题。按照我们学过的帕累托80/20定律,系统绝大多数的时间应该都耗费在少量的代码片段里面。如何找出这些需要优化的代码呢?唯一可靠的办法就是profile。现代流行的各种编程语言,比如Java和Python等,都有相关的profile工具。所以,会熟练使用这些profile工具是性能分析和优化的必要条件。
|
||||
|
||||
我们举几个例子,比如Java语言,就有很多工具,像JVMTI(JVM Tools Interface,JVM工具接口),就为性能分析器提供了方便的钩子,可以用来跟踪诸如函数调用、线程相关的事件、类加载之类的事件。再比如对Python语言来说,我们可以用sys.setprofile函数,跟踪Python的函数调用返回异常等事件。
|
||||
|
||||
## 总结
|
||||
|
||||
我们的儒家思想提倡“格物致知”,就是说要深入探究事物的原理,而从中获得知识和智慧。性能优化能否成功,也需要探究一个系统中性能的真正问题,找到性能的最大瓶颈。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/61/af/613abf15528375121c11570320080eaf.png" alt="">
|
||||
|
||||
格物致知时,还需要“正心“和”诚意”,就是要实事求是和端正态度,从而科学而系统地收获知识。我们做性能分析时候,也是需要根据实际的数据和Profiling等测试结果,找到性能的瓶颈,并合理地解决性能问题。
|
||||
|
||||
## 思考题
|
||||
|
||||
你工作中有没有碰到没有搞明白的性能问题?如果有,想想能不能按照今天讲的几个可能的性能问题领域来一个个考虑并验证一下?说不定会有“守得云开见月明”的恍然大悟呢。
|
||||
|
||||
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。
|
||||
136
极客时间专栏/性能工程高手课/性能分析/15 | CPU篇:如何让CPU的运行不受阻碍?.md
Normal file
136
极客时间专栏/性能工程高手课/性能分析/15 | CPU篇:如何让CPU的运行不受阻碍?.md
Normal file
@@ -0,0 +1,136 @@
|
||||
<audio id="audio" title="15 | CPU篇:如何让CPU的运行不受阻碍?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ce/60/cedd5a8624ba5169aa3e4661de8ba360.mp3"></audio>
|
||||
|
||||
你好,我是庄振运。
|
||||
|
||||
从这一讲开始,我们探讨分析几个最容易发生性能问题的领域:CPU、内存、存储和网络。
|
||||
|
||||
这一讲先来讨论关于CPU的常见性能问题。首先我们从硬件的角度,来看看CPU的性能取决于哪些因素,然后分析一下CPU的内部结构。接着我们探讨和CPU性能相关的软件系统,看看CPU运行时侯的调度和切换。
|
||||
|
||||
## CPU的性能决定因素
|
||||
|
||||
宏观来讲,一台服务器里面的CPU性能取决于好几个因素,包括有多少处理器、多少个核、时钟主频是多少、有没有Turbo模式、处理器内部的运算架构以及和CPU紧密交互的其他部件的性能。
|
||||
|
||||
CPU的更新换代很频繁,基本上每两年就会更新一代。比如Intel的CPU,最近10年已经经历了5代左右。每一代都有主频的变化,而且有好几个变种。
|
||||
|
||||
下面的表格描述了从十年前(也就是2009年)的SandyBridge,到后来的IvyBridge、Haswell、Broadwell,直到Skylake。注意,对后面的三代,我分别列出了其中的两种变化——单处理器(1P)和双处理器(2P)。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/0c/09/0ccfee296bbd3ab792b41ee0feb26209.png" alt="">
|
||||
|
||||
大体上我们可以看出,虽然CPU更新换代,但是处理器的时钟主频基本不再提高,甚至变得更低了。这样的目的是降低CPU的功耗。比如SandyBridge的时钟频率是2.6GHz,但是到了Skylake,反而降低到了2GHz。
|
||||
|
||||
为了提升单个处理器的性能,每个处理器里面的核数却越来越多,这样就可以尽量的提升并行处理能力。比如SandyBridge的每个处理器只有8个核,而Skylake则多达20个核。
|
||||
|
||||
而且我们也看到,每一代CPU都允许Turbo模式,就是让CPU的主频提高。目的是可以让处理器在特殊情况下,用提高功耗的代价来增加主频,从而获得更高性能。
|
||||
|
||||
## CPU的内部结构
|
||||
|
||||
CPU的性能也取决于它的内部结构设计。很多程序员对CPU的内部机构不是完全清楚,尤其是对相关的术语之间的区别和联系一知半解,比如多处理器和多核、逻辑CPU和硬件线程、超线程,以及L1/L2/L3三级缓存等。
|
||||
|
||||
之所以对这些结构不甚了解,主要原因是现代处理器变得复杂,普遍采用多处理器,多核以及内部的各种优化处理来提高CPU性能。我们今天就从外到内,从宏观到微观地介绍一下。
|
||||
|
||||
我注意到,这方面的很多中文术语,大家有时候用法不一致,所以很容易混淆。为了清楚描述,你尤其要注意一下我用的术语(包括英文)。
|
||||
|
||||
### 多处理器和NUMA
|
||||
|
||||
现在的CPU普遍采用多处理器(Socket)来提高CPU性能,每个处理器都有自己可以直接访问的本地内存(Local Memory)。一般来讲,这里面每个处理器的性能和内存大小都是一样的。每个处理器也都可以访问其他处理器的内存,这些内存就相当于是外地/远程内存(Remote Memory)。
|
||||
|
||||
当CPU处理器访问本地内存时,会有较短的响应时间(称为本地访问Local Access)。而如果需要访问外地/远程内存时候,就需要通过互联通道访问,响应时间就相比本地内存变慢了(称为远端访问Remote Access)。所以NUMA(Non-Uniform Memory Access)就此得名。
|
||||
|
||||
下图展示了两个处理器的NUMA架构。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/88/33/88b6a75956af211654f2fe6c13a0c933.png" alt="">
|
||||
|
||||
如果处理器A访问内存A,就是本地访问。如果它访问内存B,就是远端访问,内存的访问延迟大大增加。
|
||||
|
||||
采用多处理器和NUMA架构的主要原因,是提高整个CPU的并行处理性能。每一台服务器可以同时运行很多程序和进程。对每一个进程和线程而言,当它运行在某一个处理器上时,它所对应的内存使用默认的分配方案是——优先尝试在请求线程当前所处的处理器的本地内存上分配。如果本地内存不足,才会分配到外地/远程内存上去。
|
||||
|
||||
### 多核结构和多级缓存
|
||||
|
||||
我们再看看每个处理器内部的结构。我们刚刚讲到的处理器,内部一般都是多核(Core)架构。随着多核处理器的发展,CPU的缓存通常分成了三个级别:L1、L2和L3。
|
||||
|
||||
级别越小就越接近CPU,速度更快,同时容量也越小。L1和L2一般在核的内部,我们下一讲还会详细讲。L3缓存是三级缓存中最大的一级,同时也是最慢的一级;在同一个处理器内部的核会共享同一个 L3 缓存。
|
||||
|
||||
除了多个核以及L3缓存外,处理器上一般还有非核心处理器(Uncore),里面含有和指令运行不直接相关的组件,包括QPI控制器和存储器一致性监测组件,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/35/e7/354764adfca130abdc7ae15a0fafd0e7.png" alt="">
|
||||
|
||||
### 超线程(Hyperthreading,HT)
|
||||
|
||||
一个核还可以进一步分成几个逻辑核,来执行多个控制流程,这样可以进一步提高并行程度,这一技术就叫超线程,有时叫做 simultaneous multi-threading(SMT)。
|
||||
|
||||
超线程技术主要的出发点是,当处理器在运行一个线程,执行指令代码时,很多时候处理器并不会使用到全部的计算能力,部分计算能力就会处于空闲状态。而超线程技术就是通过多线程来进一步“压榨”处理器。
|
||||
|
||||
举个例子,如果一个线程运行过程中,必须要等到一些数据加载到缓存中以后才能继续执行,此时CPU就可以切换到另一个线程,去执行其他指令,而不用去处于空闲状态,等待当前线程的数据加载完毕。
|
||||
|
||||
通常,一个传统的处理器在线程之间切换,可能需要几万个时钟周期。而一个具有HT超线程技术的处理器只需要1个时钟周期。因此就大大减小了线程之间切换的成本,从而最大限度地让处理器满负荷运转。
|
||||
|
||||
一个核分成几个超线程呢?
|
||||
|
||||
这个数字会根据CPU架构有所变化;Intel一般是把一个核分成2个。
|
||||
|
||||
“这台计算机有多少CPU?”
|
||||
|
||||
我们经常会问这个问题,结合我们刚刚讲的知识,就很容易回答了。
|
||||
|
||||
比如,如果一台计算机有两个处理器,每个处理器有12个核,而且采用了HT超线程,那么总的CPU数目就是48,就是2×12×2。这个数字48,就是我们平时用监控软件和命令看到的CPU的数量。比如,Linux的top或者vmstat命令,显示的CPU个数就是这样算出来的。
|
||||
|
||||
## CPU性能指标和常见性能问题
|
||||
|
||||
我们继续探讨CPU的性能指标和常见性能问题,这方面很多资料都有涉及,我们提纲挈领地总结一下。
|
||||
|
||||
最表层的CPU性能指标,就是CPU的负载情况和使用率。CPU使用率又进一步分成系统CPU、用户CPU、IO等待CPU等几个指标。你执行一下top命令就会看到。
|
||||
|
||||
需要注意的是,因为CPU架构的复杂性,以及和其他部件的交互,CPU的使用率和负载的关系往往不是线性的。
|
||||
|
||||
也就是说,如果10%的CPU使用率可以每秒处理1千个请求,那么80%的CPU使用率能够处理多少请求呢?不太可能处理每秒8千个请求,往往会远远小于这个数字。
|
||||
|
||||
衡量一个应用程序对CPU使用效率时,往往会考察CPI(Cycles Per Instruction,每指令的周期数)和 IPC(Instructions Per Cycle,每周期的指令数)。这两个指标有助于识别运行效率高或低的应用程序。而一台计算机的CPU性能一般用MIPS(Millions of Instructions Per Second)来衡量,表示每秒能运行多少个百万指令,MIPS越高,性能越高。MIPS的计算很简单,就是时钟频率×IPC。
|
||||
|
||||
继续往深处分析,CPU常见的各种中断包括软中断和硬中断。除此之外,还有一种特殊的中断:上下文切换。这些指标需要和每个核挂钩,理想情况下是各个核上的中断能够均衡。如果数量不均衡,往往会造成严重的性能问题——有的核会超载而导致系统响应缓慢,但是其他的核反而空闲。
|
||||
|
||||
和CPU相关的性能问题,基本上就是表现为CPU超载或者空闲。
|
||||
|
||||
如果是CPU超载,那么就要分析为什么超载。多数情况下都不一定是合理的超载,比如说多核之间的负载没有平衡好,或者CPU干了很多没用的活,或者应用程序本身的设计需要优化等等。反之,如果是CPU空闲,那就需要了解为什么空闲,或许是指令里面太多内存数据操作,从而造成CPU停顿,也或许是太多的分支预测错误等,这就需要具体分析和对症下药的优化。
|
||||
|
||||
CPU对多线程的执行顺序是谁定的呢?
|
||||
|
||||
是由内核的进程调度来决定的。内核进程调度负责管理和分配CPU资源,合理决定哪个进程该使用 CPU,哪个进程该等待。进程调度给不同的线程和任务分配了不同的优先级,优先级最高的是硬件中断,其次是内核(系统)进程,最后是用户进程。每个逻辑CPU都维护着一个可运行队列,用来存放可运行的线程来调度。
|
||||
|
||||
## CPU的性能监测工具
|
||||
|
||||
我们最后讲一下CPU性能监测方面的工具。和CPU监测相关的工具挺多的,而且往往每个工具都包含很多不同的有用的信息。
|
||||
|
||||
比如在Linux上,最常用的Top系统进程监控命令。Top是一个万金油的工具,可以显示出CPU的使用、内存的使用、交换内存和缓存大小、缓冲区大小、各个进程信息等。
|
||||
|
||||
如果想要查看过去的CPU负载情况,可以用uptime。也可以用mpstat和pidstat,来分别查看每个核还有每个进程的情况。另一个常用的vmstat命令可以用于显示虚拟内存、内核线程、磁盘、系统进程、I/O模块、中断等信息。
|
||||
|
||||
对有经验的性能工程师来讲,有一个类似于“瑞士军刀”一样的好工具:Perf。
|
||||
|
||||
Perf是Linux上的性能剖析(profiling)工具,极为有用。它是基于事件采样原理,以性能事件为基础,利用内核中的计数器来进行性能统计。它不但可以分析指定应用程序的性能问题,也可以用来分析内核的性能问题。
|
||||
|
||||
## 总结
|
||||
|
||||
这一讲我们讨论了计算机的运算核心,CPU的结构,尤其是它内部的和性能相关的部件,并澄清了一些术语。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b7/7c/b7600ae6757369212e30aec6e829997c.png" alt="">
|
||||
|
||||
CPU是服务器性能的最重要的部分;因为不管程序代码如何优化,最后都要转换成指令,让CPU来执行。不管其他部件如何和CPU交互,最终目的是让CPU尽快地拿到指令,并满载执行。
|
||||
|
||||
唐代有个诗人叫李贺,他曾经形容跨马奔驰的愿景:
|
||||
|
||||
“大漠沙如雪,燕山月似钩。<br>
|
||||
何当金络脑,快走踏清秋。”
|
||||
|
||||
我们常把CPU类比大脑,CPU性能优化的目标,就是让它的运行不受阻碍,如千里马一样任意驰骋。
|
||||
|
||||
现代CPU提升性能的主要途径是并行化,这方面的策略包括:多处理器、多核、超线程,另外还有流水线架构和超标量等等,都是为了提高并行处理能力。
|
||||
|
||||
## 思考题
|
||||
|
||||
你工作中一定碰到过CPU方面的性能问题吧?总结一下,有几种表现形式?
|
||||
|
||||
>
|
||||
比如是总体CPU使用量太高,还是计算机的几个核负载不均衡?负载不均衡是什么原因导致的呢?是线程数不够,还是系统调度的问题?
|
||||
|
||||
|
||||
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。
|
||||
129
极客时间专栏/性能工程高手课/性能分析/16 | 内存篇:如何减少延迟提升内存分配效率?.md
Normal file
129
极客时间专栏/性能工程高手课/性能分析/16 | 内存篇:如何减少延迟提升内存分配效率?.md
Normal file
@@ -0,0 +1,129 @@
|
||||
<audio id="audio" title="16 | 内存篇:如何减少延迟提升内存分配效率?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/70/ec/706dfa50bd94f96ac1b38c51ab81beec.mp3"></audio>
|
||||
|
||||
你好,我是庄振运。
|
||||
|
||||
上一讲我们讨论了关于CPU的性能指标和分析。CPU和内存是和程序性能最相关的两个领域;那么这一讲,我们就来讨论和内存相关的性能指标和性能分析的工具。
|
||||
|
||||
内存方面的性能指标,主要有缓存命中率、缓存一致性、内存带宽、内存延迟、内存的使用大小及碎片、内存的分配和回收速度等,接下来我会逐一进行介绍。现代很多CPU都是NUMA架构的,所以我也会介绍NUMA的影响和常用的工具。
|
||||
|
||||
## 缓存和缓存命中率
|
||||
|
||||
我们先看看缓存,也就是Cache。
|
||||
|
||||
缓存是CPU与内存之间的临时数据交换器,是为了解决两种速度不匹配的矛盾而设计的。这个矛盾就是**CPU运行处理速度**与**内存读写速度**不匹配的矛盾。CPU处理指令的速度,比内存的速度快得多了,有百倍的差别,这一点我们已经在上一讲讨论过。
|
||||
|
||||
缓存的概念极为重要。不止是CPU,缓存的策略也用在计算机和互联网服务中很多其他的地方,比如外部存储、文件系统,以及程序设计上。有人甚至开玩笑说,计算机的各种技术说到底就是三种——Cache(缓存)、Hash(哈希处理)和Trash(资源回收)。这种说法当然有点偏颇,但你也能从中看到缓存技术的重要性。
|
||||
|
||||
现在回到CPU缓存的讨论上来。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/75/53/754b694aed8f28c2e215876fc596cf53.png" alt="">
|
||||
|
||||
我们前面也讲了,随着多核CPU的发展,CPU缓存通常分成了三个级别:L1、L2、L3。一般而言,每个核上都有L1和L2缓存。L1缓存其实分成两部分:一个用于存数据,也就是L1d Cache(Data Cache),另外一个用于存指令,L1i Cache(Instruction Cache)。
|
||||
|
||||
L1缓存相对较小,每部分差不多只有几十KB。L2缓存更大一些,有几百KB,速度就要慢一些了。L2一般是一个统一的缓存,不把数据和指令分开。L3缓存则是三级缓存中最大的一级,可以达到几个MB大小,同时也是最慢的一级了。你要注意,在同一个处理器上,所有核共享一个L3缓存。
|
||||
|
||||
为什么要采用多级缓存,并逐级增加缓存大小呢?
|
||||
|
||||
这个目的,就是为了提高各级缓存的命中率,从而最大限度地降低直接访问内存的概率。每一级缓存的命中率都很重要,尤其是L1的命中率。这是因为缓存的命中率对总体的访问时间延迟影响很大,而且下一级缓存的访问延迟往往是上一级缓存延迟的很多倍。
|
||||
|
||||
为了加深你的理解,我还是用文章中图片里的延迟数据来举例说明一下。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/75/53/754b694aed8f28c2e215876fc596cf53.png" alt="">
|
||||
|
||||
在图片里你可以看到,L1的访问时间是3个时钟周期,L2的访问时间是12个时钟周期。假如在理想情况下,L1i的命中率是100%,就是说每条指令都从L1i里面取;那么平均指令访问时间也是3个时钟周期。作为对比,如果L1i命中率变成90%,也就是只有90%的指令从L1i里面取到,而剩下的10%需要L2来提供。
|
||||
|
||||
那么平均指令访问时间就变成了3.9个指令周期(也就是:90%*3+10%*12)。虽然看起来只有10%的指令没有命中,但是相对于L1命中率100%的情况,平均访问时间延迟差不多增大了多少呢?高达30%。
|
||||
|
||||
## 缓存一致性
|
||||
|
||||
虽然缓存能够极大地提升运算性能,但也带来了一些其他的问题,比如“缓存一致性问题(cache coherence)”。
|
||||
|
||||
如果不能保证缓存一致性,就可能造成结果错误。因为每个核都有自己的L1和L2缓存,当在不同核上运行同一个进程的不同线程时,如果这些线程同时操作同一个进程内存,就可能互相冲突,最终产生错误的结果。
|
||||
|
||||
举个例子,你可以设想这样一个场景,假设处理器有两个核,core-A和core-B。这两个核同时运行两个线程,都操作共同的变量,i。假设它们并行执行i++。如果i的初始值是0,当两个线程执行完毕后,我们预期的结果是i变成2。但是,如果不采取必要的措施,那么在实际执行中就可能会出错,什么样的错误呢?我们来探讨一下。
|
||||
|
||||
运行开始时,每个核都存储了i的值0。当第core-A做i++的时候,其缓存中的值变成了1,i需要马上回写到内存,内存之中的i也就变成了1。但是core-B缓存中的i值依然是0,当运行在它上面的线程执行i++,然后回写到内存时,就会覆盖core-A内核的操作,使得最终i 的结果是1,而不是预期中的2。
|
||||
|
||||
为了达到数据访问的一致,就需要各个处理器和内核,在访问缓存和写回内存时遵循一些协议,这样的协议就叫**缓存一致性协议**。常见的缓存一致性协议有MSI、MESI等。
|
||||
|
||||
缓存一致性协议解决了缓存内容不一致的问题,但同时也造成了缓存性能的下降。在有些情况下性能还会受到严重影响。我们下一讲还会仔细分析这一点,并且讨论怎样通过优化代码来克服这样的性能问题。
|
||||
|
||||
## 内存带宽和延迟
|
||||
|
||||
我们讨论了缓存,接下来探讨内存带宽和内存访问延迟。
|
||||
|
||||
计算机性能方面的一个趋势就是,内存越来越变成主要的性能瓶颈。内存对性能的制约包括三个方面:内存大小、内存访问延迟和内存带宽。
|
||||
|
||||
第一个方面就是内存的使用大小,这个最直观,大家都懂。这方面的优化方式也有很多,包括采用高效的,使用内存少的算法和数据结构。
|
||||
|
||||
第二个方面是内存访问延迟,这个也比较好理解,我们刚刚讨论的各级缓存,都是为了降低内存的直接访问,从而间接地降低内存访问延迟的。如果我们尽量降低数据和程序的大小,那么各级缓存的命中率也会相应地提高,这是因为缓存可以覆盖的代码和数据比例会增大。
|
||||
|
||||
第三个方面就是内存带宽,也就是单位时间内,可以并行读取或写入内存的数据量,通常以字节/秒为单位表示。一款CPU的最大内存带宽往往是有限而确定的。并且一般来说,这个最大内存带宽只是个理论最大值,实际中我们的程序使用只能达到最大带宽利用率的60%。如果超出这个百分比,内存的访问延迟会急剧上升。
|
||||
|
||||
文章中的图片就展示了几款Intel的CPU的内存访问延迟和内存带宽的关系(图片来自[https://images.anandtech.com](https://images.anandtech.com))。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a1/7b/a1a03d7bdcb021be9c4653d0ef49ae7b.png" alt="">
|
||||
|
||||
图中内存带宽使用是横轴,相对应的,内存访问延迟是纵轴。你可以清楚地看到,当内存带宽较小时,内存访问延迟很小,而且基本固定,最多缓慢上升。而当内存带宽超过一定值后,访问延迟会快速上升,最终增加到不能接受的程度。
|
||||
|
||||
那么一款处理器的内存总带宽取决于哪些因素呢?
|
||||
|
||||
答案是,有四个因素,内存总带宽的大小就是这些因素的乘积。这四个因素是:DRAM时钟频率、每时钟的数据传输次数、内存总线带宽(一般是64bit)、内存通道数量。
|
||||
|
||||
我们来用几个实际的Intel CPU为例,来看看内存带宽的变化。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b1/55/b16a437792c79f5c2c195fd632206255.png" alt="">
|
||||
|
||||
文章中的这个表格大体上总结了5款Intel CPU的各级缓存大小、内存通道数目、可使用内存带宽(这里取最大值的50%)、内存频率和速度。你可以看到,每款新的CPU,它的内存带宽一般还是增加的,这主要归功于内存频率的提升。
|
||||
|
||||
## 内存的分配
|
||||
|
||||
讲过内存带宽,我们再来看看内存的分配。程序使用的内存大小很关键,是影响一个程序性能的重要因素,所以我们应该尽量对程序的内存使用大小进行调优,从而让程序尽量少地使用内存。
|
||||
|
||||
不知道你有没有过系统内存用光的经验?每当发生这种情况,系统就会被迫杀掉一些进程,并且抛出一个系统错误:内存用光“OOM(Out of memory)”。所以,一个应用程序用的内存越少,那么OOM错误就越不太可能发生。
|
||||
|
||||
还有,服务器等容量也是公司运营成本的一部分。如果一台服务器的内存资源足够,那么这样一个服务器系统就可以同时运行多个程序或进程,以最大限度地提高系统利用率,这样就节省了公司运营成本。
|
||||
|
||||
再进一步讲,应用程序向操作系统申请内存时,系统会分配内存,这中间总要花些时间,因为操作系统需要查看可用内存并分配。一个系统的空闲内存越多,应用程序向操作系统申请内存的时候,就越快地拿到所申请的内存。反之,应用程序就有可能经历很大的内存请求分配延迟。
|
||||
|
||||
比如说,在系统空闲内存很少的时候,程序很可能会变得超级慢。因为操作系统对内存请求进行(比如malloc())处理时,如果空闲内存不够,系统需要采取措施回收内存,这个过程可能会阻塞。
|
||||
|
||||
我们写程序时,或许习惯直接使用new、malloc等API申请分配内存,直观又方便。但这样做有个很大的缺点,就是所申请内存块的大小不定。当这样的内存申请频繁操作时,会造成大量的内存碎片;这些内存碎片会导致系统性能下降。
|
||||
|
||||
一般来讲,开发应用程序时,采用内存池(Memory Pool)可以看作是一种内存分配方式的优化。
|
||||
|
||||
所谓的内存池,就是提前申请分配一定数量的、大小仔细考虑的内存块留作备用。当线程有新的内存需求时,就从内存池中分出一部分内存块。如果已分配的内存块不够,那么可以继续申请新的内存块。同样,线程释放的内存也暂时不返还给操作系统,而是放在内存池内留着备用。
|
||||
|
||||
这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率和系统的总体内存使用效率得到提升。
|
||||
|
||||
## NUMA的影响
|
||||
|
||||
我们刚刚谈了内存性能的几个方面,最后看看多处理器使用内存的情景,也就是NUMA场景。NUMA系统现在非常普遍,它和CPU和内存的性能都很相关。简单来说,NUMA包含多个处理器(或者节点),它们之间通过高速互连网络连接而成。每个处理器都有自己的本地内存,但所有处理器可以访问全部内存。
|
||||
|
||||
因为访问远端内存的延迟远远大于本地内存访问,操作系统的设计已经将内存分布的特点考虑进去了。比如一个线程运行在一个处理器中,那么为这个线程所分配的内存,一般是该处理器的本地内存,而不是外部内存。但是,在特殊情况下,比如本地内存已经用光,那就只能分配远端内存。
|
||||
|
||||
我们部署应用程序时,最好将访问相同数据的多个线程放在相同的处理器上。根据情况,有时候也需要强制去绑定线程到某个节点或者CPU核上。
|
||||
|
||||
## 工具
|
||||
|
||||
内存相关的工具也挺多的。比如,你最熟的内存监测命令或许是free了。这个命令会简单地报告总的内存、使用的内存、空闲内存等。
|
||||
|
||||
vmstat(Virtual Meomory Statistics, 虚拟内存统计)也是Linux中监控内存的常用工具,可以对操作系统的虚拟内存、进程、CPU等的整体情况进行监视。
|
||||
|
||||
我建议你也尽量熟悉一下Linux下的/proc文件系统。这是一个虚拟文件系统,只存在内存当中,而不占用外存空间。这个目录下有很多文件,每一个文件的内容都是动态创建的。这些文件提供了一种在Linux内核空间和用户间之间进行通信的方法。比如/proc/meminfo就对内存方面的监测非常有用。这个文件里面有几十个条目,比如SwapFree,显示的是空闲swap总量等。
|
||||
|
||||
另外,/proc这个目录下还可以根据进程的ID来查看每个进程的详细信息,包括分配到进程的内存使用。比如/proc/PID/maps文件,里面的每一行都描述进程或线程中连续虚拟内存的区域;这些信息提供了更深层次的内存剖析。
|
||||
|
||||
## 总结
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/19/db/19ec66b033e2fd90d3d8123bc66b19db.png" alt="">
|
||||
|
||||
我们都知道宋代词人辛弃疾,他曾经这样憧憬他的战场梦想:“马作的卢飞快,弓如霹雳弦惊。” 我们开发的应用程序对内存的分配请求延迟,也有相似的期盼,就是要动作飞快。如果内存分配延迟太大,整个程序的性能自然也高不上去。
|
||||
|
||||
如何实现这个梦想呢?就需要我们的代码和程序,尽量降低对内存的使用大小和内存带宽,尽量少地请求分配和释放内存,帮助系统内存状态不至于太过碎片化,并且对代码结构做一些相应地优化。
|
||||
|
||||
## 思考题
|
||||
|
||||
你正在开发的系统或者模块,会运行在什么样的服务器上?服务器上有多少内存?如果内存大小可能不够,你会采取什么措施来降低内存使用量呢?再进一步,内存带宽会是瓶颈吗?
|
||||
|
||||
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。
|
||||
159
极客时间专栏/性能工程高手课/性能分析/17 | 存储篇:如何根据性能优缺点选择最合适的存储系统?.md
Normal file
159
极客时间专栏/性能工程高手课/性能分析/17 | 存储篇:如何根据性能优缺点选择最合适的存储系统?.md
Normal file
@@ -0,0 +1,159 @@
|
||||
<audio id="audio" title="17 | 存储篇:如何根据性能优缺点选择最合适的存储系统?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/89/a7/897a66f0c3cf12fa85d0802b7bb579a7.mp3"></audio>
|
||||
|
||||
你好,我是庄振运。
|
||||
|
||||
前面两讲我们讨论了CPU和内存,今天我们讨论第三个重要的主题:存储系统。现在是大数据时代,这些数据终归要保存到各种存储系统里面,以供读写和分析,因此讨论存储系统的性能问题就很有必要了。
|
||||
|
||||
狭义上的存储往往是硬件,比如磁盘、磁带还有固态硬盘。而广义上的存储系统除了指硬件的硬盘,还包括基于网络的存储系统,比如SAN(Storage Area Network, 存储区域网络)和NAS存储(Network Attached Storage,网络接入存储)。
|
||||
|
||||
各种存储系统各有优缺点,尤其是性能和成本,所以对不同的需求,我们要选择最合适的存储系统。
|
||||
|
||||
我们首先讲存储系统最重要的三大性能指标:IOPS、访问延迟和带宽,然后讲传统硬盘HDD(Hard Disk Drive)的性能。因为传统硬盘的特性相对简单直白(毕竟业界已经用了几十年了)。这之后再讲固态硬盘的性能(固态硬盘就是SSD,也叫Flash)。相对于传统硬盘,SSD的内部工作原理很不一样,这也就导致它们的性能特性大相径庭。 最后,我们再延伸到基于网络的存储系统,并且介绍几个常用的和存储相关的工具。
|
||||
|
||||
## 存储系统的三大性能指标
|
||||
|
||||
一个存储系统的性能最主要的是三个:**IOPS**、**访问延迟**、**吞吐率/带宽**。这三个指标其实是互相关联和影响的,但是我们一般还是分开来衡量。
|
||||
|
||||
**IOPS**(Input/Output Per Second),即每秒钟能处理的读写请求数量,这是衡量存储性能的主要指标之一。每个IO的请求都有自己的特性,比如读还是写,是顺序读写还是随机读写,IO的大小是多少等。
|
||||
|
||||
什么是顺序读写呢?就是访问存储设备中相邻位置的数据;随机读写呢,则是访问存储设备中非相邻位置的数据。对随机读写进行性能衡量时,一般假定IO大小是4KB。
|
||||
|
||||
既然IO有这些特点,所以我们讨论存储系统IOPS性能的时候,经常需要更加具体的描述。比如顺序读IOPS、随机写IOPS等。
|
||||
|
||||
IOPS的数值会随这样的参数不同而有很大的不同,这些参数的变化,包括读取和写入的比例、其中顺序读写及随机读写的比例、读写大小、线程数量及读写队列深度等。此外,系统配置等因素也会影响IOPS的结果,例如操作系统的设置、存储设备的驱动程序特点、操作系统后台运行的作业等。
|
||||
|
||||
**访问延迟**(Access Time)和**响应时间**(Response Time),指的是从发起IO请求,到存储系统把IO处理完成的时间间隔,常以毫秒(ms)或者微妙(us)为单位。对这一性能指标,我们通常会考虑它的平均值和高位百分数,比如P99、P95。
|
||||
|
||||
**吞吐率**(Throughput)或者带宽(Bandwidth),衡量的是存储系统的实际数据传输速率,通常以MB/s或GB/s为单位。一般来讲,IOPS与吞吐率是紧密相关的;它们之间的关系是,吞吐率等于IOPS和IO大小的乘积。
|
||||
|
||||
这个也很容易理解,比如对一个硬盘的读写IO是1MB,硬盘的IOPS是100,那么硬盘总的吞吐率就是100MB/s。需要强调的是,这里IO的具体特性很重要,比如是顺序还是随机,IO大小等。
|
||||
|
||||
还有一点要注意,有些存储系统会因为其**IO队列深度增加**,而获得更好的IO性能;比如吞吐率会升高,平均访问延迟会降低。
|
||||
|
||||
这是为什么呢?这是因为存储系统的IO队列处理机制,可以对IO进行重新排序,从而获得好的性能。比如,它可以合并几个相邻的IO,把随机IO重新排序为顺序IO等。
|
||||
|
||||
## HDD(传统硬盘)的性能
|
||||
|
||||
我们先从你熟知的传统硬盘开始讨论。对于传统硬盘,我们应该比较熟悉它的内部是如何操作的。
|
||||
|
||||
简单来说,当应用程序发出硬盘IO请求后,这个请求就会进入硬盘的IO队列。如果前面有其他IO,那么这个请求可能需要排队等待。当轮到这个IO来存取数据时,磁头需要机械运动到数据存放的位置,这就需要磁头寻址到相应的磁道,并旋转到相应的扇区,然后才是数据的传输。所以,讨论硬盘IO的性能时,需要充分考虑这一点。
|
||||
|
||||
我们有时候需要把**硬盘响应时间**和**硬盘访问时间**分开对待。它们之间的关系是,硬盘响应时间除了包括访问时间外,还包括IO排队的延迟,如下图所示。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/03/19/030b5f050f8230d70654dd1df78c3119.png" alt="">
|
||||
|
||||
我们如果拿起一块硬盘仔细看看,硬盘上面往往会标注后面三个参数,分别是平均寻址时间、盘片旋转速度,以及数据传输速度,这三个参数就可以提供给我们计算上述三个步骤的时间。
|
||||
|
||||
平均寻址时间一般是几个毫秒。平均旋转时间可以从硬盘转动速度RPM来算出。因为每个IO请求平均下来需要转半圈,那么如果硬盘磁头每分钟转一万圈(10K RPM),转半圈就需要3毫秒。
|
||||
|
||||
要注意的是,硬盘上面标注的数据传输速度参数往往是最大值,实际的数据传输时间要取决于IO的大小。
|
||||
|
||||
对于一块普通硬盘而言,我们前面讲常用的性能数字时也提过,随机IO读写延迟就是8毫秒左右,IO带宽大约每秒100MB,而随机IOPS一般是100左右。
|
||||
|
||||
硬盘的技术也在发展,现代的硬盘也有很多变种。比如采用了多磁头技术,或者几块硬盘组成磁盘阵列,这样的整体IO性能也会相应地提升。
|
||||
|
||||
## SSD(固态硬盘)的技术背景
|
||||
|
||||
讲完了传统硬盘,我们接着看看固态硬盘——SSD。SSD的内部工作方式和HDD大相径庭,我们来了解一下。
|
||||
|
||||
### 单元(Cell)、页面(Page)、块(Block)
|
||||
|
||||
当今的主流SSD是基于NAND的,它是将数字位存储在单元中。每个SSD单元可以存储一位(SLC,Single Level Cell,单级单元)、两位(MLC,多级单元)、三位(TLC,三级单元),甚至四位(QLC)。
|
||||
|
||||
SSD的特点是,对SSD单元的每次擦除,都会降低单元的寿命,因此每一个单元只能承受一定数量的擦除。所以,不同的SSD就有这几方面的考虑和平衡。单元存储的位数越多,制造成本就越少,SSD的容量也就越大。但是耐久性(擦除次数)也会降低。所以高端的SSD,比如企业级的,基本都是基于SLC的。
|
||||
|
||||
一个页面包括很多单元,典型的页面大小是4KB。页面也是读写的最小存储单位。我们知道,HDD可以直接对任何字节重写和覆盖;但是对SSD而言,不能直接进行上述的“覆盖”操作。SSD的一个页面里面的所有单元,一旦写入内容后就不能进行重写,必须和其它相邻页面一起,被整体擦除、重置。
|
||||
|
||||
在SSD内部,多个页面会组合成**块**。一个块的典型大小为512KB或1MB,也就是大约128或256页。块是擦除的基本单位,每次擦除,都是整个块内的所有页面都被重置。
|
||||
|
||||
### I/O和垃圾回收(Garbage Collection)
|
||||
|
||||
我们总结一下,对SSD的IO操作,一共有三种类型:**读取**、**写入**和**擦除**。读取和写入是以页为单位的,也就是说最少也要读取写入一个页面。
|
||||
|
||||
IO写入的延迟,具体取决于磁盘的历史状态,因为如果SSD已经存储了许多数据,那么对页的写入,有时需要移动已有的数据,这种情况下写入延迟就比较大。但多数情况下,读写延迟都很低,一般在微秒级别,远远低于HDD。
|
||||
|
||||
擦除是以块为单位。擦除速度相对很慢,通常为几毫秒。所以,对同步的IO请求,发出IO的应用程序,可能会因为块的擦除而经历很大的写入延迟。为了尽量地减少这样的场景发生,一块SSD最好保持一定数量的空闲块,这样可以保证SSD的写入速度足够快。
|
||||
|
||||
SSD内部有垃圾回收(GC)机制,它的目的就在于此,就是不断回收不用的块,进行擦除,从而产生新的空闲块来备用。这样可以确保以后的页写入能快速分配到一个全新的页。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/b3/6e/b3e76cd89e5471b21900c489f839396e.png" alt="">
|
||||
|
||||
### 写入放大(Write Amplification, or WA)
|
||||
|
||||
这是SSD相对于HDD的一个缺点,即实际写入SSD的物理数据量,有可能是应用层写入数据量的多倍。
|
||||
|
||||
这是因为,一方面页级别的写入需要移动已有的数据来腾空页面来写入。另一方面,GC的操作,也会移动用户数据来进行块级别的擦除。
|
||||
|
||||
所以,对SSD真正的写操作的数据,肯定比实际写的数据量大,这就是写入放大。因为一块SSD只能进行有限的擦除次数,也称为编程/擦除(P/E)周期,所以写入放大效用会缩短SSD的寿命。
|
||||
|
||||
### 耗损平衡(Wear Leveling)
|
||||
|
||||
对每一个块而言,一旦擦除造成的损耗达到最大数量,该块就会“死亡”,再也不能存储数据了。对于SLC类型的块,P/E周期的典型数目是十万次;对于MLC块,P/E周期的数目是一万;而对于TLC块,则可能是几千。为了确保整块SSD的容量、性能和可靠性,SSD内部需要对整个SSD的各块做平衡,尽量在擦除次数上保持类似。
|
||||
|
||||
SSD控制器具有这样一种机制,也叫“耗损平衡”,来实现这一目标。在损耗平衡操作时,数据在各个块之间移动,以实现均衡的损耗。但是这种机制也有害处,就是会对前面讲的写入放大推波助澜。
|
||||
|
||||
## SSD的性能和应用程序的设计
|
||||
|
||||
性能方面,SSD的IO性能相对于HDD来说,IOPS和访问延迟提升了上千倍,吞吐率也是提高了几十倍。但是SSD的缺点也很明显。主要有三个缺点:
|
||||
|
||||
1. 贵;
|
||||
1. 容量小;
|
||||
1. 易损耗。
|
||||
|
||||
好消息是,随着技术的发展,这三个缺点近几年在弱化。
|
||||
|
||||
如今,越来越多的应用程序采用SSD来减轻I/O性能瓶颈。许多测试和实践的结果都表明,与HDD相比,采用SSD带来了极大的应用程序性能提升。
|
||||
|
||||
但是,我想强调的一点是,在大多数采用SSD的部署方案中,SSD仅被视为一种“更快的HDD”,并没有真正发挥SSD的潜力。
|
||||
|
||||
我为什么这么说呢?因为尽管使用SSD作为存储时,应用程序可以获得更好的性能,但是这些收益,主要归因于**SSD提供的更高的IOPS和带宽**。
|
||||
|
||||
但是,SSD除了提供这些之外,它还有其它特点,比如易损耗,以及其独特的内部机制。如果应用程序的设计能充分考虑SSD的内部机制,设计出对SSD友好的应用程序,就可以更大程度地优化SSD,从而进一步提高应用程序性能,也可以延长SSD的寿命,并降低运营成本。关于这方面,我后面会有一讲专门讨论。
|
||||
|
||||
## 基于网络的存储系统
|
||||
|
||||
我们前面讨论了存储硬件,这些存储硬件可以直接安装在服务器上,构成单机系统。和单机系统和场景相对应,也有很多非单机使用的场景。
|
||||
|
||||
在非单机使用的场景里,这些存储硬件也被包装在各种基于网络的存储系统里。这样的存储系统也有很多种,比如DAS、NAS和SAN。
|
||||
|
||||
DAS(Directed Attached Storage)是直连式存储。这是以服务器为中心的存储系统,存储设备直接通过I/O总线连在服务器主机上。这种存储一般运行SATA或者SAS等协议,可以让网络的客户端直接使用。
|
||||
|
||||
NAS(Network Attached Storage)是网络接入存储。在NAS存储结构中,存储系统不再通过I/O总线只属于某个特定的服务器,而是通过网络接口直接与网络相连。NAS提供的是文件服务器的功能(比如NFS和CIFS),供客户通过网络访问。
|
||||
|
||||
SAN(Storage Area Network)是存储区域网络。SAN是一种以网络为中心的存储系统,通常有高性能专用网络(比如光纤)来支持,运行iSCSI等协议。
|
||||
|
||||
## 工具
|
||||
|
||||
最后,我们看看常用的存储系统性能监测和测试工具。存储系统的测试和监控命令工具非常多,下面简单介绍几个。
|
||||
|
||||
Linux系统上可以采用fio工具进行各种组合的IO测试。这些组合包括读写比例、随机还是顺序读写、IO大小等等。
|
||||
|
||||
IOMeter也是不错的测试磁盘性能的工具,比如可以测试I/O的传输速度和平均的I/O响应时间。
|
||||
|
||||
IOZone是一个文件系统基准测试工具,可以测试不同的操作系统中文件系统的读写性能。
|
||||
|
||||
Bonnie++是基于Linux平台的开源磁盘IO测试的工具,可以用它来测试磁盘和文件系统的I/O性能。
|
||||
|
||||
hdparm可以用来作跳过文件系统的纯硬件操作测试。
|
||||
|
||||
iostat这个工具可以查看进程发出IO请求的数量、系统处理IO请求的耗时、磁盘的利用率等,也可以分析进程与操作系统的交互过程中IO方面是否存在瓶颈。
|
||||
|
||||
## 总结
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/a9/b3/a946f03c8cee8ac2a5ed4e0b875716b3.png" alt="">
|
||||
|
||||
存储系统,顾名思义,是用来存放我们各种程序和服务的数据。随着现代互联网服务的大数据化,几乎所有的业务都离不开存储系统的支持。
|
||||
|
||||
各种存储系统的基础是传统硬盘或者固态硬盘,这两种硬盘在成本、性能、大小方面各有千秋。我想起了宋代有首诗叫《雪梅》,里面比较了白雪和梅花的优缺点:
|
||||
|
||||
“梅须逊雪三分白,雪却输梅一段香。”
|
||||
|
||||
这句诗用来形容传统硬盘和固态硬盘的关系还挺合适的。
|
||||
|
||||
在实际使用中,我们要注意它们的性能优缺点,从而适当来作取舍。比如,如果系统对IOPS或者延迟要求很高,恐怕只有SSD才能满足要求。反之,如果数据量极大需要降低成本,那么只能选择磁盘或者磁带系统了。
|
||||
|
||||
## 思考题
|
||||
|
||||
你对SSD这种新型存储了解多少?你公司里面有没有系统使用SSD?它们碰到的性能问题有哪几个方面?运维和开发人员是怎么一起解决这些问题的?
|
||||
|
||||
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。
|
||||
153
极客时间专栏/性能工程高手课/性能分析/18 | 网络篇:如何步步拆解处理复杂的网络性能问题?.md
Normal file
153
极客时间专栏/性能工程高手课/性能分析/18 | 网络篇:如何步步拆解处理复杂的网络性能问题?.md
Normal file
@@ -0,0 +1,153 @@
|
||||
<audio id="audio" title="18 | 网络篇:如何步步拆解处理复杂的网络性能问题?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/8d/54/8dff554130dcd56ba4649eb6cfbf0c54.mp3"></audio>
|
||||
|
||||
你好,我是庄振运。
|
||||
|
||||
前面几讲,我们讨论了CPU、内存和存储系统的性能。不过你也清楚,各种互联网服务的数据传递终归是需要通过网络来传输的,所以网络性能也是至关重要的。所以,今天我们就来接着学习网络性能相关的内容。
|
||||
|
||||
这一讲我的讲解,依然是会逐步递进,先介绍有关网络的几个性能指标,并从单机的网络性能说起,之后推进到数据中心内部的网络性能,接着再进一步阐释互联网和内容分发网络的性能。最后,再为你介绍几个常用网络性能工具。
|
||||
|
||||
## 网络的性能指标
|
||||
|
||||
先来说说网络的性能指标。网络性能的五个常用的指标是:
|
||||
|
||||
1. 可用性(availability)
|
||||
1. 响应时间(response time)
|
||||
1. 网络带宽容量(network bandwidth capacity)
|
||||
1. 网络吞吐量(network throughput)
|
||||
1. 网络利用率(network utilization)
|
||||
|
||||
第一个指标是**可用性**,理解起来比较简单。你肯定有过网络不通(也就是断网)的糟糕体验。没有网络,你几乎啥都干不了。所以,对于网络来讲,最重要的就是,网络是否可以正常联通。
|
||||
|
||||
如何测试网络可用性呢?
|
||||
|
||||
最简单的方法,就是**使用ping命令**。这个命令其实就是向远端的机器发送ICMP的请求数据包,并等待接收对方的回复。通过请求和应答返回的对比,来判断远端的机器是否连通,也就是网络是否正常工作。
|
||||
|
||||
第二个指标是**响应时间**。端到端的数据一次往返所花费时间,就是响应时间。响应时间受很多因素的影响,比如端到端的物理距离、所经过网络以及负荷、两端主机的负荷等。
|
||||
|
||||
第三个指标是**网络带宽容量**。它指的是在网络的两个节点之间的最大可用带宽,这一指标一般是由设备和网络协议决定的,比如网卡、局域网和TCP/IP的特性。如果是向网络提供商购买的带宽,那么购买的数量就是网络的带宽容量。
|
||||
|
||||
第四个指标**网络吞吐量**是指在某个时刻,在网络中的两个节点之间,端到端的实际传输速度。网络吞吐量取决于当前的网络负载情况,而且是随着时间不同而不断变化的。
|
||||
|
||||
第五个指标**网络利用率**是指网络被使用的时间占总时间的比例,一般以百分比来表示。因为数据传输的突发性,所以实际中的网络利用率一般不会太高。否则的话,那么响应时间就不能保证。
|
||||
|
||||
## 单机的网络性能
|
||||
|
||||
了解了网络方面性能的指标后,我们接着讨论具体的网络性能。虽然网络传输需要两端进行,但是我们必须从单机开始,来清楚地了解网络的协议栈。
|
||||
|
||||
网络协议其实相当复杂,而且分很多层级。操作系统内核中,最大的一个子系统或许就是网络。网络子系统由多个协议组成,其中每个协议都在更原始的协议之上工作。大学时你应该学过OSI模型,或TCP / IP协议栈,里面的分层定义对网络协议非常适用。当用户数据通过网络协议栈传递时,数据会被封装在该协议的数据包中。
|
||||
|
||||
在考虑多层协议的交互时,我们尽量把思路简化。其实它们之间的关系很简单,就是网络协议栈的每一层都有其职责,与其他高层和低层协议无关。
|
||||
|
||||
举个例子来说,我们看IP层的协议。IP是第三层协议,它是通过路由器和网络发送端到端的数据报;它的主要目的,就是在网络中的每一段找到路由路径,从而最终能够到达数据报的接收地。但它不保证数据的有序性,也就是转发过程中几个数据报会重新排序;同时也不能保证整个数据的完整性和可靠性,比如丢失的数据报那就是丢失了,IP层不会重传。
|
||||
|
||||
那么数据的可靠性是谁保证的呢?这就需要更上层的TCP协议实现。TCP层协议通过检测数据丢失并且重传,来保证数据的可靠性,也通过序列号来保证数据的有序性。
|
||||
|
||||
但是这两层协议:TCP和IP两层协议,都只能传输原始的数据;而对于数据本身是否被压缩过,这些数据表示什么,是在更高层的协议(如HTTP和应用层)上实现的。
|
||||
|
||||
我们接着具体到网络协议的程序实现,在Unix系统中,网络协议栈可以大体分为三层。从上到下,第一层是通过一系列系统调用,实现BSD套接字的套接字层,比如sendmsg()函数;第二层是中间协议的程序,例如TCP/IP/UDP;第三层是底部的媒体访问控制层,提供对网络接口卡(NIC)本身的访问。
|
||||
|
||||
如下图所示(图片来自于[https://myaut.github.io/](https://myaut.github.io/) )。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/13/72/13ac1f15762144348da5c170c2b95f72.png" alt="">
|
||||
|
||||
注意这个图,右边是发送端,左边是接收端。先从右边看起,最上层就是BSD套接字。它通过一系列API调用(比如connect()等),然后是TCP的发送,再到IP的发送,最后到网卡的发送缓冲区,并最终通过网卡发出。 接收端,也就是图的左边,则会经过相反的顺序,逐层到达应用层。
|
||||
|
||||
## 数据中心的网络性能
|
||||
|
||||
谈完单机的网络性能,我们接着讨论端到端的互联网数据传输。
|
||||
|
||||
一台服务器和互联网的远端服务器进行数据传输的时候,需要经过好几层交换器和不同的网络。数据从一台服务器的网卡出来之后,下一步就是经过机柜上面的交换器,然后是数据中心内部的网络,再进入互联网骨干网络。接收端的情况正好相反。
|
||||
|
||||
对于这些中间的网络构件,我们一个一个地讨论一下。
|
||||
|
||||
**机柜交换器**(TOR, Top Of Rack; or RSW, Rack Switch)
|
||||
|
||||
数据中心里面的服务器不是单独放置的,一般是几十台服务器组成了一个机柜。机柜上面会有机柜交换器。这个机柜交换器的作用,一方面让机柜内部的服务器直接互通;另一方面,机柜交换器会有外联线路,连接到数据中心的骨干网络。
|
||||
|
||||
**数据中心网络**
|
||||
|
||||
数据中心网络里面也分了好几层,从TOR到集群交换器(Cluter Switch),再到集合交换器(Aggregation Switch)等,最后到数据中心路由器。
|
||||
|
||||
下面的图示,简单展示了机柜内部的网络和POD内部网络。TOR1是机柜交换器,负责机柜内部几十台服务器之间的数据交换。POD1内部网络包含很多机柜。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/7e/a7/7e8c8b6513c0456d5d71a6f9a8da5fa7.png" alt="">
|
||||
|
||||
这里有一个值得注意的地方是,就是这个多层次结构,一般是越往上层,总的带宽越少。
|
||||
|
||||
比如,服务器的网卡带宽是25Gbps,即使这个机柜内有30台服务器,总带宽就是750Gbps,机柜交换器TOR的外联带宽有多少呢?可能只有100Gbps。这个差距就叫**带宽超订**(over-subscription)。同样的,POD交换器之间的带宽会继续变小。之所以允许带宽超订,是因为多数的数据交换是在内部进行的,不会全部都和外部进行交换。
|
||||
|
||||
知道这一点是必要的,因为我们做网络方面和服务部署优化的时候,需要考虑这点,不要让高层的网络带宽成为性能瓶颈。比如假设两个服务分别用不同的服务器,它们之间的数据交换如果很多的话,就尽量让它们运行在同一个机柜的服务器里面;如果不能保证同一个机柜,就尽量是同一个POD内部。
|
||||
|
||||
## 互联网的网络性能
|
||||
|
||||
讲完了数据中心内部的网络,我们继续往外扩展,讨论互联网的网络性能。互联网上运行的是TCP/IP协议。这两个协议本身比较复杂,有很多和性能相关的特性值得仔细学习。
|
||||
|
||||
一个常见的性能问题就是**丢包**。TCP对丢包非常敏感,因为每次丢包,TCP都认为是网络发生了拥塞,因此就会降低传输速度,并且采取重传来恢复,这就影响网络性能。
|
||||
|
||||
实际情况中,造成丢包的原因有很多,不一定就是网络拥塞。因此我们需要进行各种测试观测来做根因分析。
|
||||
|
||||
通常的丢包原因,是**端到端的网络传输中的某一段发生了问题**,或许是拥塞,或许是硬件问题,也或许是其他软件原因。我们需要一步步地逐段逐层地排除。
|
||||
|
||||
对于网络的每段,可以用工具(比如Traceroute)来发现每一段路由,然后逐段测试。
|
||||
|
||||
对于协议的每层,你都可以用相关工具进行分析。比如,你可以分为TCP层、操作系统、网卡驱动层,分别分析。具体来讲,对TCP层,可以用比如netstat来观察是不是套接字缓存不够;对操作系统,可以观察softnet_stat,来判断CPU的查询队列;对网卡驱动层,可以用ethtool等来进行分析。
|
||||
|
||||
## 内容分发网络(CDN)的性能
|
||||
|
||||
当今互联网几乎普遍采用内容分发网络来提高网络性能。内容分发网络,也叫CDN(Content Delivery Network或Content Distribution Network,CDN),是一种分布式网络,它可以有效地将Web内容交付给用户。
|
||||
|
||||
内容分发网络的基本原理是,利用最靠近每位用户的服务器,更快、更可靠地将(音乐、图片及其他)文件发送给终端用户,而不是每次都依赖于中心服务器。靠近用户的服务器,一般叫边缘服务器,会把请求的内容最大限度地缓存,以尽量地减少延迟。
|
||||
|
||||
我们用下图讲述它的基本工作方式,图里有两个用户和一个边缘服务器(Edge Server),以及一个源头服务器(Origin Server)。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/8c/75/8c882c90c0f6bdfb19ce255a76ad8575.png" alt="">
|
||||
|
||||
第1步:用户A通过使用具有特殊域名的URL来请求文件。DNS将请求重定向到性能最佳的边缘服务器,该位置通常是地理位置上最接近用户的服务器。
|
||||
|
||||
第2步:如果重定向的边缘服务器A中没有文件,则边缘服务器会向源头服务器请求该文件。源头服务器可以是任何可公开或非公开访问的Web服务器。
|
||||
|
||||
第3步:源头服务器将文件返回到边缘服务器。
|
||||
|
||||
第4步:边缘服务器先缓存文件,并将文件返回给原始请求者。该文件将保留在边缘服务器上,这样下次这个文件就可以迅速返回请求的客户。这个文件会保存到什么时候呢?时间由其HTTP标头指定的生存时间(TTL)到期为止。
|
||||
|
||||
第5步:如果用户B用相同URL请求相同的文件,也可能定向到相同的边缘服务器。
|
||||
|
||||
第6步:如果文件的TTL尚未过期,则边缘服务器直接从缓存中返回文件。这样的用户体验就更快。
|
||||
|
||||
内容分发网络的边缘服务器节点会在多个地点,多个不同的网络上摆放。这些节点之间通常会互相传输内容,对用户的下载行为最优化,并借此改善用户的下载速度,提高系统的稳定性。同时,将边缘服务器放到不同地点,也可以减少网络互连的流量,进而降低带宽成本。
|
||||
|
||||
内容分发网络提供商往往有很大的规模,比如几十万台服务器。对服务的客户提供所需要的节点数量会随着需求而不同。
|
||||
|
||||
## 工具
|
||||
|
||||
我们最后来看看网络性能有关的工具。这方面的性能测试和观测工具也不少,这里简单介绍几个。
|
||||
|
||||
**Netperf**是一个很有用的网络性能的测量工具,主要针对基于TCP或UDP的传输。Netperf有两种操作模式:批量数据传输和请求/应答模式,根据应用的不同,可以进行不同模式的网络性能测试,Netperf测试结果所反映的,是一个端到端的系统能够以多快的速度发送数据和接收数据。
|
||||
|
||||
**Iperf**这个工具也以测试TCP和UDP带宽质量,比如最大TCP带宽,延迟抖动和数据包丢失等性能参数。
|
||||
|
||||
**Netstat**这一命令可以显示与IP、TCP、UDP和ICMP协议相关的统计数据,提供TCP连接列表,TCP和UDP监听,进程内存管理的相关报告,一般用于检验本机各端口的网络连接情况。
|
||||
|
||||
**Traceroute**这一命令可以帮我们知道,数据包从我们的计算机到互联网远端的主机,是走的什么网络路径。
|
||||
|
||||
## 总结
|
||||
|
||||
不管计算机技术如何发展,网络永远是极其重要的一部分;而且随着互联网业务的多样化和复杂化,对网络性能的要求只会越来越高。不管应用如何变化,高性能网络必不可少,而且会变得更复杂。这让我想起了宋朝大词人张先的《千秋岁》里面的几句词,“天不老,情难绝。心似双丝网,中有千千结。”
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/06/ad/06ac9fe057487675c8c8e44f0b9622ad.png" alt="">
|
||||
|
||||
网络是整个互联网服务的一部分,各种因素之间互相影响。和网络性能演化密切相关的因素包括:上层服务需求的越来越多样化,计算机硬件和操作系统的持续进化,各层网络协议的不断优化,以及底层介质的演化(比如5G)。
|
||||
|
||||
复杂的网络性能问题会受所有这些因素的影响,似有“千千结”。所以,网络性能分析和优化,需要仔细分析和考虑这些方面。
|
||||
|
||||
我们这一讲讨论了网络性能的几个性能指标,也讨论了单机网络协议,数据中心的网络,互联网和内容分发网络。
|
||||
|
||||
对于网络性能要求,简单来说就是能联通、响应快、带宽高;并且在部署大流量服务时,可以优化服务器的部署,来尽量减少外部网络流量;也可以使用CDN来加速数据传输。
|
||||
|
||||
## 思考题
|
||||
|
||||
你们公司的互联网服务使用内容分发网络,也就是CDN吗?是哪个CDN公司提供的?
|
||||
|
||||
除了你们使用的这家CDN公司,市场上还有哪些CDN提供商?选择CDN提供商的时候,你们会考虑哪些方面吗?比如规模、带宽、价格、服务质量等等。
|
||||
|
||||
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。
|
||||
Reference in New Issue
Block a user