《构建高性能Web站点(修订版)》的笔记(作者:郭欣)

构建高性能Web站点(修订版)

郭欣

1.1 等待的真相
2017-10-12
站点服务器处理请求并生成回应数据的时间主要消耗在服务器端,包括非常多的环节,我们一般用另一个指标来衡量这部分时间,即每秒处理请求数(也称吞吐率)。注意这里的吞吐率不是指单位时间内处理的数据量,而是请求数。
2017-10-12
影响服务器吞吐率的因素非常多,比如服务器的并发策略、I/O模型、I/O性能、CPU核数等,当然也包括应用程序本身的逻辑复杂度等

1.2 瓶颈在哪里
2017-10-12
系统性能的瓶颈是指影响性能的关键因素,这个关键因素随着系统的运行又会发生不断的变化或迁移,比如,由于站点用户组成结构的多样性和习惯的差异,导致在不同时段系统的瓶颈各不相同,又如,站点在数据存储量或浏览量增长到不同级别时,系统瓶颈也会发生迁移

2.2 带宽
2017-10-16
铜线中电信号的传播速度大约为2.3×108m/s,光纤中光信号的传播速度大约为2.0×108m/s。
2017-10-16
在真空中的光速大约为3.0×108m/s,同时它不仅仅是可见光的传播速度,也是所有电磁波在真空中的传播速度。根据现代物理学,所有电磁波(包括可见光)在真空中的传播速度是常数,即是光速。
2017-10-16
那为什么光纤中的传播速度要低一些呢?这很容易理解,光纤在传播光信号的时候,利用了光的全反射原理,所以光信号在光纤中的实际传播距离要大于光纤的长度,而我们一般所说的传播速度都是针对光纤的长度。
2017-10-16
显然,我们所讲的带宽是指数据的发送速度。比如我们的百兆网卡,便是指网卡的最大发送速度为100Mbps,也就是网卡在1秒钟最多可以发出100Mb的数据。
注: 带宽是什么?数据的发送速度
2017-10-16
数据发送装置将二进制信号传送至线路的能力,也称信号传输频率,以及另一端的数据接收装置对二进制信号的接收能力,同时也包括线路对传输频率的支持程度。
2017-10-16
数据传播介质的并行度,这里也可以称为“宽度”,完全等价于计算机系统总线宽度的概念。
2017-10-16
有意思的是,要提高计算机总线的带宽,包括提高总线频率和总线宽度两种方法,比如使用64位总线系统或者使用主频更高的处理器等。
2017-10-16
事实上,数字网络通信相比于计算机内部数据传输而言,其传输距离要远远大于后者,所以它还隐藏着另一个因素,那就是信号在传播介质中的衰减,这与传播介质以及信号传播方式有着密切的关系

2.3 响应时间
2017-10-16
计算下载速度的方法是:用传输的数据字节数,除以这些数据从服务器开始发送直到完全到达用户PC的时间。形象地说,这段时间是从数据的第一个比特由服务器网卡进入线路开始,直到最后一个比特位进入用户PC的网卡为止。以上的计算方法在任意长度的时间片段中都成立,如果时间片非常短,那么计算结果就是我们一般说的实时下载速度。

3.1 吞吐率
2017-10-16
说到Web服务器的并发处理能力,那就一定得有一个量化的描述,我们一般使用单位时间内服务器处理的请求数来描述其并发处理能力,习惯称其为吞吐率(Throughput),单位是“reqs/s”
注: 吞吐率:单位时间内服务器处理的请求数

3.2 CPU并发计算
2017-10-16
在操作系统中,时间片的长度是权衡利弊后的一个博弈结果,如果各进程的时间片太短,那么CPU浪费在进程切换上的时间比例就比较大,整体效率降低;如果时间片太长,则多任务实时性以及交互性就无法保证。
2017-10-16
IOWait往往并不能真实地代表I/O操作的性能或者工作量,它的设计出发点是用来衡量CPU性能的。举个例子,假如有一个任务需要花费10ms的I/O操作时间和10ms的CPU时间,那么总时间为20ms,IOWait便为10ms/20ms,即为50%,这时并不意味着I/O操作的繁忙程度为50%,事实上,即便是IOWait为100%,也不一定代表I/O出现性能问题或者瓶颈。同样,IOWait为0的时候,I/O操作也可能很繁忙。所以,如果你希望真正了解当前I/O的性能,可以进行磁盘I/O测试或者查看网络I/O流量等。

3.3 系统调用
2017-10-17
由于系统调用涉及进程从用户态到内核态的切换,导致一定的内存空间交换,这也是一定程度上的上下文切换,所以系统调用的开销通常认为是比较昂贵的。

3.7 服务器并发策略
2017-10-18
我们知道Apache这种多进程模型的开销限制了它的并发连接数,但是Apache也有自身的优势,比如从稳定性和兼容性的角度看,多进程模型的优势正体现在它相对安全的独立进程,任何一个子进程的崩溃都不会影响Aapche本身,Apache父进程可以创建新的子进程;

4.2 缓存与速度
2017-10-18
CPU缓存面前,它还是力不从心。
顺便提一下另一个看起来相似的概念——缓冲(buffer)。缓冲的原意出自物理学,那就是减缓冲击力,在计算机应用场景中,我们使用它的引申含义,其目的在于改善各部件之间由于速度不同而引发的问题。比如将用户态地址空间的数据写入磁盘时,显然内存的速度比磁盘速度要快得多,所以人们设计了磁盘缓冲区,让数据源源不断地流进缓冲区,再由缓冲区负责写入磁盘,这样内存便可以不必随着磁盘的慢节奏来工作,所以磁盘缓冲区起到了将快速设备和慢速设备平滑衔接的作用,另外我们在线观看视频的时候,视频缓冲区的意义也是如此。
缓冲和缓存有一些相似之处,比如它们都需要一块存储区,而且它们的本质都与速度不一致有关,即便是缓存,如果计算速度和读取缓存的速度差不多,那么它也毫无意义。但是,缓存更加注重的是策略,也就是说缓存命中率

6.2 缓存协商
2017-10-23
需要注意的是,HTTP协议中规定使用GMT时间,也就是格林威治标准时间,而我们国家使用的是GMT+8时区,所以在HTTP头信息中的时间会比我们的正常时间早8个小时
2017-10-23
在使用浏览器缓存后,能否获得较大的吞吐率提升,关键在于是否能够避免一些额外的计算开销
2017-10-23
浏览器缓存并非对吞吐率不能有较大的提升,这完全取决于HTTP响应正文的长度

8.2 何为反向
2017-10-25
回想刚才提到的传统代理服务器的特点,即用户隐藏在代理服务器之后,那么,反向代理服务器的特点便与此刚好相反,那就是Web服务器隐藏在代理服务器之后。我们将这种代理机制称为反向代理(Reverse Proxy),同时,实现这种机制的服务器,便称为反向代理服务器(Reverse Proxy Server)。
注: 何为反向代理

9.4 浏览器并发数
2017-10-26
浏览器下载组件的过程受到最大并发数的限制,也就是浏览器同一时刻最多只可以下载一定数量的组件,不同的浏览器拥有不同的默认限制
2017-10-26
为什么Web组件分离后会提高浏览器的并发数呢?其实,浏览器的最大并发数限制有一个前提,那就是对于同一个域名下的组件才有效,也就是说,浏览器会为每个域名维护不同的下载队列

9.5 发挥各自的潜力
2017-10-26
动态内容的运行需要不少的内存开销,一旦物理内存不够用,内存管理器会使用swap,这导致磁盘和内存之间发生频繁的数据复制,这可不好玩,我们需要坚决避免。
2017-10-26
进程的稳定性
2017-10-26
大多数的动态内容都需要涉及数据库访问,这相当于一种远程I/O操作,它同样存在等待时间,但是不同于本地I/O等待时间,它还包括网络I/O的等待时间,前面我们曾经介绍过网络传输中响应时间的计算方法。将动态内容服务器与数据库服务器保持高速连接,可以最大化地减少网络I/O等待时间。

10.6 缓存扩展
2017-10-26
这里有一个问题也许你一直在思考,那就是当我们扩展缓存系统后,由于分区算法的改变,会涉及缓存数据需要从一台缓存服务器迁移到另一台缓存服务器的问题,如何迁移呢?事实上,根本不需要考虑分区之间的迁移,因为这是缓存,它应该具备在必要时刻牺牲自己的勇气,当然这是你赋予它的,你必须明白缓存不是持久存储,并且从引入分布式缓存开始就不断地提醒自己。
2017-10-26
没错,当调整缓存分区算法后,我们需要时间来等待缓存重建和预热,但这往往并不影响站点的正常运转,前提是你按照前面读缓存和写缓存的理念来进行设计。顺便一提的是,与此相比,数据库规模扩展引发分区(Shard)之间的数据迁移就要复杂得多,

11.2 正确使用索引
2017-10-26
在大多数情况下,索引扫描当然要比全表扫描获得更好的性能,但这并不是绝对的,如果要查找的记录占据了整个数据表的很大比例,那么使用索引扫描反而性能更差,这不难想象,对于一本书来说,如果你希望阅读80%以上的内容,那么与其每篇文章都通过目录查找,倒不如抛开目录,来一次顺序的浏览,难道不是吗?不要忘了查找目录也需要时间开销啊。
2017-10-26
数据库中的查询优化器会判断一次查询是否有必要使用现有的索引以及使用哪个索引。当然,有些时候优化器也会犯错误,我们需要为它指引道路,比如在组合索引存在的时候,事情就不那么简单了
2017-10-26
索引本身的数据结构(MySQL使用BTree、Hash以及RTree)决定了它们拥有非常高效的查找算法,我们基本上不用担心这部分的开销。
2017-10-26
一般来说,如果一个字段出现在查询语句中基于行的选择、过滤或排序条件中,那么为该字段建立索引便是有价值的,但这也不是绝对的。
2017-10-26
另一方面,对于没有使用主键索引或者唯一索引的条件查询,查询结果可能会有多个匹配行,MySQL为这种情况定义的type为ref,它在联合查询中也很常见。
2017-10-26
因为一次查询对于一个数据表只能使用一个索引,它们是无法进行效用叠加的。这样一来,便会存在一定程度的局部行扫描(Range Scan),这在有些特定的场景中将严重影响查询性能,比如上述第一条查询,数据库会先利用字段a的索引快速匹配a=1的记录,然后在这些记录中筛选b=2的记录,而此时b字段的索引将爱莫能助
2017-10-26
“最左前缀”这个组合索引的基本原则
2017-10-26
Using filesort和Using temporary非常不受欢迎,它们越少越好。

12.2 HTTP重定向
2017-10-26
事实上凡是HTTP请求处理逻辑本身可以独立决策的调度策略,都拥有较高的性能表现
2017-10-26
负载反馈

12.4 反向代理负载均衡
2017-10-26
在基于反向代理的负载均衡系统中,我们也常把实际服务器称为后端服务器(Back-end Server)。
2017-10-27
在实际应用中,为了提高整个负载均衡系统的可用性而不影响性能,我们可以部署一定数量的备用后端服务器,这样即便是一些后端服务器出现故障后被调度器放弃,备用后端服务器也可以接替它们的工作,保证整体的性能。

12.5 IP负载均衡
2017-10-27
在基于NAT的负载均衡系统中,作为调度器的NAT服务器可以将吞吐率继续提升到一个新的高度,几乎是反向代理服务器的两倍以上,这当然归功于在内核中进行请求转发的较低开销。

12.6 直接路由
2017-10-27
相比于LVS-NAT,LVS-DR的优势在哪里呢?还记得这种方式的最大特点吗?那就是实际服务器的响应数据包可以不经过调度器而直接发往用户端
2017-10-27
幸运的是,对于LVS-DR,一旦调度器失效,你可以马上将LVS-DR切换到DNS-RR模式,这几乎只需要增加几条DNS记录,将域名解析到多台实际服务器的真实IP地址即可。一旦调度器恢复后,你便可以再次修改DNS记录,将域名仅指向调度器,切换回LVS-DR。

15.1 文件系统
2017-10-29
另一方面,出于整体性能以及降低复杂度的考虑,分布式文件系统并没有设计得面面俱到,比如通常我们不能对分布式文件系统中的文件进行修改,因为这样会涉及锁定和版本,必然增加大量额外开销,而事实上,对于大多数Web应用,我们可以很容易地规避文件修改,而采用增加副本的方式,比如用户修改照片实际上等同于上传新的照片。

16.1 复制和分离
2017-10-29
这种复制是异步进行的,从服务器定时向主服务器请求最新日志,而主服务器只需要通过一个I/O线程来读取本地二进制日志,并输送给从服务器即可,所以,复制过程对于主服务器的影响非常有限。但是,当存在多个从服务器同时从一个主服务器进行复制的时候,主服务器的磁盘压力会有不同程度的增长,这也并不是无法解决的,你可以采用多级复制策略,就像前面提到的多级分发一样。

16.2 垂直分区
2017-10-29
对于数据库写操作频繁(write-heavy)的站点来说,仅仅采用主从复制和读写分离可能效果并不明显
2017-10-29
另一方面,站点在成长,用户活动越来越频繁,写操作不断增多,主服务器的压力也逐渐接近极限,这时候不论你增加多少台从服务器都无济于事,因为那只是对读操作的分散,并没有对写操作起到任何作用。
2017-10-29
最简单的分离方法当然是将不同的数据库分布到不同的服务器上,你会发现有很多数据库之间并不存在关系,或者不需要进行联合(JOIN)查询,那么为什么不把它们放在不同的服务器上呢?比如我们将用户的博客数据库和好友数据库分别转移到独立的数据库服务器上,这种方式称为垂直分区

16.3 水平分区
2017-10-29
水平分区(Sharding)意味着我们可以将同一数据表中的记录通过特定的算法进行分离,分别保存在不同的数据表中,从而可以部署在不同的数据库服务器上。
2017-10-29
当然,在我们不得不对一些数据进行分区的时候,也意味着我们将失去一些操作它们的能力,比如原本你可以通过一条联合查询语句就能轻松搞定的任务,在分区后,你必须先通过用户ID找到正确的分区,然后查到对应的好友ID,再通过好友ID找到对应的分区,从而找到好友信息
2017-10-29
一旦我们知道要对哪些数据实施分区后,接下来就得找到一个用于分区的字段,我们称为分区索引字段,比如前面的user_id,它必须和所有的记录都存在关系,一般我们会用被分区数据的主键或者外键。当使用主键时,你得保证它不能使用auto_increment自增类型。
2017-10-29
相对于其他算法,哈希算法可以为多个分区比较均衡地分配工作量,特别是当记录数量级较多时,各个分区更加趋近于均衡。但是,这种算法对于扩展并不友好,一旦我们需要从10个分区扩展到20个分区,这便涉及所有数据的重新分区,你不得不暂停站点,等待漫长的计算。
2017-10-29
显然,它可以带来很好的扩展性,随着用户数量的不断增长,我们可以创建更多的分区。但是,各个分区
的工作量会存在较大的差异,比如老用户所在的分区压力相对较大,或者一部分ID比较接近的热点用户导致所在分区压力过大。
2017-10-29
分区的可扩展性更多体现在能否快速平滑地实现扩展,并且进行最少单位的数据移动。
2017-10-29
通过Web负载均衡的体验,我们发现,计算能力是一种内在的、无形的力量,它的扩展本身几乎不需要时间,而数据库水平分区的扩展则意味着必要的数据迁移,以及重建平衡,这种流动带来了时间开销。

17.2 并行计算
2017-10-29
然而,对于分布式计算来说,异步计算才只是刚刚开始,一个现实的问题在于,即便我们将一个耗时的计算从Web服务进程中剥离并转移到其他的服务器上,但这仅仅是转移,并不能减少它的计算时间,而这个任务可能恰恰是我们需要快速得到结果的,这该怎么办呢?
2017-10-29
正如前面提到的从分到合,它的思想是伟大的,Map/Reduce正是对它进行了精辟的总结和抽象,它认为任何的计算任务都可以经历从拆分到汇总的两个过程,反过来,只需要用这两个过程就可以描述所有的计算任务,这两个过程分别为Map和Reduce。
2017-10-29
有一个问题在于,假如某个Worker Server上的Map操作领取了计算任务后,突然发生了故障,比如I/O缓慢或者CPU不可用等,这将导致它无法完成计算,必然会拖累整个计算过程,大家不能浪费时间来等待它,这属于掉队者问题,解决办法是,将同样的计算任务分配给多个Map操作,实现冗余计算,一旦获得结果后,终止其他计算
2017-10-29
另一方面,在并行计算的过程中,必然会涉及大量的数据移动,在刚才的例子中,主要的数据便是学生的试卷内容,往往在实际应用中,我们会对数据进行压缩传输

你可能感兴趣的:(《构建高性能Web站点(修订版)》的笔记(作者:郭欣))