This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

View 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性能、QPIQuickPath Interconnect处理器之间的快速通道互联和缓存内存。
1. 存储和外部IO包括处理器的IO的接口性能、各种存储系统尤其是HDD和SSD性能
1. 网络包括服务器到机柜交换机的网络、数据中心的网络、CDN和互联网。
总体上来讲,性能分析的目的,是提供高性能、低延迟、高效率的服务。
要实现这一目的,就需要找到系统和服务的性能瓶颈,然后尽可能的消除瓶颈,或者降低瓶颈带来的影响。系统和服务有性能瓶颈就说明这个地方的资源不够用了。所谓最大的性能瓶颈,就是说这个地方的资源短缺程度最大,相对而言,其他地方的资源有富余。
如何找到最大的性能瓶颈?
这就需要进行性能测试和性能分析了。性能分析时需要知道三个层次的知识:
第一个层次是可能的性能瓶颈,比如我们刚刚讨论的十大瓶颈。知道了瓶颈才能有目标的去分析。
第二个层次是每个瓶颈有哪些资源有可能短缺。比如内存就有很多种不同的资源,不仅仅是简单的内存大小。除了内存使用量,还有内存带宽和内存访问延迟。
第三个层次是对每个瓶颈的每种资源要了解它和其他模块是如何交互的,对整个系统性能是如何影响的,它的正常值和极限值是多少,如何分析测量等等。
找到性能最大瓶颈后,具体的优化方式就是什么资源不够就加什么资源,同时尽量降低资源消耗,这样就可以做到在资源总量一定的情况下,有能力支撑更高的吞吐率和实现更低的延迟。
## 依据数据和剖析Profiling来分析
做性能分析时,必须采用科学的方法,尽量依据数据,来引导我们的分析和验证我们的推论,而不是完全凭空猜测。
当我们有了比较多的性能分析和优化的经验后,慢慢就会对一个系统的内部各个模块的交互,以及各种性能问题肚里有数了。这种时候,适度地做一些理论推测是合理的。就像一个有经验的医生,往往稍微了解一下病人的情况,就猜个八九不离十。这就是经验的重要性。
不过,再有经验的医生,还是需要做进一步的检验,尤其是面对复杂的病人和病情。同样的,无论性能分析的经验多丰富,我们也需要谨慎地做性能测试和数据分析,尤其是在针对重要系统的时候。
这一点可以说是性能分析和优化的第一原则。当我们怀疑性能有问题的时候应该通过合理的测试、日志分析并作合适的剖析Profillig来分析出哪里有问题从而有的放矢而不是凭感觉、撞运气。
比如如果是CPU相关的性能问题。按照我们学过的帕累托80/20定律系统绝大多数的时间应该都耗费在少量的代码片段里面。如何找出这些需要优化的代码呢唯一可靠的办法就是profile。现代流行的各种编程语言比如Java和Python等都有相关的profile工具。所以会熟练使用这些profile工具是性能分析和优化的必要条件。
我们举几个例子比如Java语言就有很多工具像JVMTIJVM Tools InterfaceJVM工具接口就为性能分析器提供了方便的钩子可以用来跟踪诸如函数调用、线程相关的事件、类加载之类的事件。再比如对Python语言来说我们可以用sys.setprofile函数跟踪Python的函数调用返回异常等事件。
## 总结
我们的儒家思想提倡“格物致知”,就是说要深入探究事物的原理,而从中获得知识和智慧。性能优化能否成功,也需要探究一个系统中性能的真正问题,找到性能的最大瓶颈。
<img src="https://static001.geekbang.org/resource/image/61/af/613abf15528375121c11570320080eaf.png" alt="">
格物致知时还需要“正心“和”诚意”就是要实事求是和端正态度从而科学而系统地收获知识。我们做性能分析时候也是需要根据实际的数据和Profiling等测试结果找到性能的瓶颈并合理地解决性能问题。
## 思考题
你工作中有没有碰到没有搞明白的性能问题?如果有,想想能不能按照今天讲的几个可能的性能问题领域来一个个考虑并验证一下?说不定会有“守得云开见月明”的恍然大悟呢。
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。

View 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。所以NUMANon-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="">
### 超线程HyperthreadingHT
一个核还可以进一步分成几个逻辑核,来执行多个控制流程,这样可以进一步提高并行程度,这一技术就叫超线程,有时叫做 simultaneous multi-threadingSMT
超线程技术主要的出发点是,当处理器在运行一个线程,执行指令代码时,很多时候处理器并不会使用到全部的计算能力,部分计算能力就会处于空闲状态。而超线程技术就是通过多线程来进一步“压榨”处理器。
举个例子如果一个线程运行过程中必须要等到一些数据加载到缓存中以后才能继续执行此时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使用效率时往往会考察CPICycles Per Instruction每指令的周期数和 IPCInstructions Per Cycle每周期的指令数。这两个指标有助于识别运行效率高或低的应用程序。而一台计算机的CPU性能一般用MIPSMillions 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使用量太高还是计算机的几个核负载不均衡负载不均衡是什么原因导致的呢是线程数不够还是系统调度的问题
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。

View 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 CacheData Cache另外一个用于存指令L1i CacheInstruction 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++的时候其缓存中的值变成了1i需要马上回写到内存内存之中的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它的内存带宽一般还是增加的这主要归功于内存频率的提升。
## 内存的分配
讲过内存带宽,我们再来看看内存的分配。程序使用的内存大小很关键,是影响一个程序性能的重要因素,所以我们应该尽量对程序的内存使用大小进行调优,从而让程序尽量少地使用内存。
不知道你有没有过系统内存用光的经验每当发生这种情况系统就会被迫杀掉一些进程并且抛出一个系统错误内存用光“OOMOut of memory”。所以一个应用程序用的内存越少那么OOM错误就越不太可能发生。
还有,服务器等容量也是公司运营成本的一部分。如果一台服务器的内存资源足够,那么这样一个服务器系统就可以同时运行多个程序或进程,以最大限度地提高系统利用率,这样就节省了公司运营成本。
再进一步讲,应用程序向操作系统申请内存时,系统会分配内存,这中间总要花些时间,因为操作系统需要查看可用内存并分配。一个系统的空闲内存越多,应用程序向操作系统申请内存的时候,就越快地拿到所申请的内存。反之,应用程序就有可能经历很大的内存请求分配延迟。
比如说在系统空闲内存很少的时候程序很可能会变得超级慢。因为操作系统对内存请求进行比如malloc())处理时,如果空闲内存不够,系统需要采取措施回收内存,这个过程可能会阻塞。
我们写程序时或许习惯直接使用new、malloc等API申请分配内存直观又方便。但这样做有个很大的缺点就是所申请内存块的大小不定。当这样的内存申请频繁操作时会造成大量的内存碎片这些内存碎片会导致系统性能下降。
一般来讲开发应用程序时采用内存池Memory Pool可以看作是一种内存分配方式的优化。
所谓的内存池,就是提前申请分配一定数量的、大小仔细考虑的内存块留作备用。当线程有新的内存需求时,就从内存池中分出一部分内存块。如果已分配的内存块不够,那么可以继续申请新的内存块。同样,线程释放的内存也暂时不返还给操作系统,而是放在内存池内留着备用。
这样做的一个显著优点是尽量避免了内存碎片,使得内存分配效率和系统的总体内存使用效率得到提升。
## NUMA的影响
我们刚刚谈了内存性能的几个方面最后看看多处理器使用内存的情景也就是NUMA场景。NUMA系统现在非常普遍它和CPU和内存的性能都很相关。简单来说NUMA包含多个处理器或者节点它们之间通过高速互连网络连接而成。每个处理器都有自己的本地内存但所有处理器可以访问全部内存。
因为访问远端内存的延迟远远大于本地内存访问,操作系统的设计已经将内存分布的特点考虑进去了。比如一个线程运行在一个处理器中,那么为这个线程所分配的内存,一般是该处理器的本地内存,而不是外部内存。但是,在特殊情况下,比如本地内存已经用光,那就只能分配远端内存。
我们部署应用程序时最好将访问相同数据的多个线程放在相同的处理器上。根据情况有时候也需要强制去绑定线程到某个节点或者CPU核上。
## 工具
内存相关的工具也挺多的。比如你最熟的内存监测命令或许是free了。这个命令会简单地报告总的内存、使用的内存、空闲内存等。
vmstatVirtual 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="">
我们都知道宋代词人辛弃疾,他曾经这样憧憬他的战场梦想:“马作的卢飞快,弓如霹雳弦惊。” 我们开发的应用程序对内存的分配请求延迟,也有相似的期盼,就是要动作飞快。如果内存分配延迟太大,整个程序的性能自然也高不上去。
如何实现这个梦想呢?就需要我们的代码和程序,尽量降低对内存的使用大小和内存带宽,尽量少地请求分配和释放内存,帮助系统内存状态不至于太过碎片化,并且对代码结构做一些相应地优化。
## 思考题
你正在开发的系统或者模块,会运行在什么样的服务器上?服务器上有多少内存?如果内存大小可能不够,你会采取什么措施来降低内存使用量呢?再进一步,内存带宽会是瓶颈吗?
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。

View 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和内存今天我们讨论第三个重要的主题存储系统。现在是大数据时代这些数据终归要保存到各种存储系统里面以供读写和分析因此讨论存储系统的性能问题就很有必要了。
狭义上的存储往往是硬件比如磁盘、磁带还有固态硬盘。而广义上的存储系统除了指硬件的硬盘还包括基于网络的存储系统比如SANStorage Area Network, 存储区域网络和NAS存储Network Attached Storage网络接入存储
各种存储系统各有优缺点,尤其是性能和成本,所以对不同的需求,我们要选择最合适的存储系统。
我们首先讲存储系统最重要的三大性能指标IOPS、访问延迟和带宽然后讲传统硬盘HDDHard 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单元可以存储一位SLCSingle 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。
DASDirected Attached Storage是直连式存储。这是以服务器为中心的存储系统存储设备直接通过I/O总线连在服务器主机上。这种存储一般运行SATA或者SAS等协议可以让网络的客户端直接使用。
NASNetwork Attached Storage是网络接入存储。在NAS存储结构中存储系统不再通过I/O总线只属于某个特定的服务器而是通过网络接口直接与网络相连。NAS提供的是文件服务器的功能比如NFS和CIFS供客户通过网络访问。
SANStorage 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它们碰到的性能问题有哪几个方面运维和开发人员是怎么一起解决这些问题的
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。

View 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的性能
当今互联网几乎普遍采用内容分发网络来提高网络性能。内容分发网络也叫CDNContent Delivery Network或Content Distribution NetworkCDN是一种分布式网络它可以有效地将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提供商的时候你们会考虑哪些方面吗比如规模、带宽、价格、服务质量等等。
欢迎你在留言区分享自己的思考,与我和其他同学一起讨论,也欢迎你把文章分享给自己的朋友。