大型网站架构核心要素之性能:高性能架构

前言

上节我们讲了架构设计的五大核心要素,今天我们就讲第一个核心要素:性能,性能是一个网站给用户最直接的感受,是一个网站的灵魂。下面我们将从以下几个方面谈谈如何构建一个高性能架构:网站性能测试web前端优化应用服务器优化存储性能优化

 

网站性能测试

性能测试是性能优化的前提和基础,也是性能优化结果的检验和度量标准,不同视角下的网站有不同的标准,也对应不同的优化。下面我们将依次展开:

不同视角下的网站性能

  • 用户视角:对于用户而言,发出一个请求,到响应结束所用的时间就是最直观的性能表现,如下图就是一个用户对性能最直观的体现方式

        主要包括计算机和网站服务器通信的时间,网站服务器处理请求的时间,

        计算机浏览器构造请求解析和响应的时间,主要优化手段是使用一些前端

        架构优化方案,例如优化HTML样式,利用浏览器的缓存和异步特性调整

        浏览器缓存策略,使用cdn服务和反向代理手段等;

  • 开发人员:开发人员主要关注的是应用程序本身及其相关子系统的性能,主要包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标;主要的优化手段有使用缓存加速数据读取,使用集群提高吞吐量,使用异步消息加快请求响应及实现削峰,使用代码优化程序自身;

  • 运维人员:运维人员主要关注基础设施性能和资源的利用率,主要包括网络运营商的带宽,服务器硬件的配置,数据中心网络架构,服务器和网络带宽的利用率等;主要的优化手段有建设优化骨干网络,使用高性价比定制服务器配置,利用虚拟化技术优化资源利用;

性能测试指标

  • 响应时间:指应用执行一个操作需要的时间,包括从请求开始到请求最后响应数据所需的所有时间。响应时间是系统最重要的性能指标,直观的反映了系统的“快慢”;

  • 并发数:指系统能够同时处理请求的数目,这个数量也反映了系统负载的特性,对于网站而言,并发数就是网站并发用户数,即同时提交请求的用户数量。与网站并发数对应的还有网站系统用户数(可以访问系统的总用户数,一般为系统的注册用户数)和网站在线用户数(当前登录系统的用户总数),这三者的关系:网站系统用户数>>网站在线用户数>>网站并发用户数;

  • 吞吐量:指单位时间内处理的请求数量,体现系统的整体处理能力。主要通过TPS(每秒事务数)、HPS(每秒HTTP请求数)和QPS(每秒查询数)来体现

  • 性能计数器:指描述服务器或者操作系统性能的一些数据指标,主要包括系统负载(System Load)、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等;

    • 系统负载:指当前正在被CPU执行和等待被CPU执行的进程数总和,是反映系统忙闲程度的重要指标。多核CPU情况下,最理想的是所有CPU都在使用,没有进程在等待被处理,此时load的理想值就是CPU的数目;当load<cpu数时,表示CPU有空闲,存在资源浪费;当load>cpu数时,表示有进程在排队等待CPU调度,系统资源不足,影响应用程序的执行性能,Linux中使用top查看系统负载。

性能测试方法

  • 性能测试:系统设计初期规划的性能指标为预期目标,在对系统不断施加压力,验证系统在资源可接受范围内是否能达到性能预期目标;

  • 负载测试:对系统不断增加并发请求以增加系统压力,直到系统的某项性能达到安全临界值,如果某个资源已经饱和状态时,继续对系统施加压力的话,系统的处理能力不但不能提升,反而会下降;

  • 压力测试:当系统超过安全负载的时候,对系统继续施加压力,直到系统崩溃或者不能再处理任何请求,以此来获取系统最大压力承受能力;

  • 稳定性测试:被测试的系统在特定的硬件、软件、网络环境下,给系统加载一定业务压力,使系统运行一段较长时间,以此来检测系统是否稳定;

 

 

性能优化策略

如果性能测试的结果不能满足业务需求,或者说不是很令人满意,那么就要进行性能分析和优化了

  • 性能分析:排查网站性能和程序性能瓶颈的基本方法是,检查请求处理的各个环节的日志,分析哪个环节响应时间不合理,超过了预期;然后检查监控数据,分析影响性能的主要因素是内存、磁盘、网络还是CPU,是代码问题还是架构不合理,或者系统资源确实不足;

  • 性能优化:根据网站分层架构,可以把性能优化也分为web前端性能优化、应用服务器性能优化、存储服务器性能优化;

 

web前端性能优化

一般来说web前端指的是网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、cdn服务等,优化手段主要体现在浏览器访问、反向代理和CDN等几个方面:

  • 浏览器访问优化

    • 减少http请求:HTTP协议都是无状态的应用层协议,意味着每次http请求都需要建立通信链路,进行数据传输,而在服务端,每个http请求都需要开启一个独立的线程去处理,增加了服务器的开销,所以减少HTTP请求的数目可以有效的提高访问性能;减少http请求的主要手段包括合并css、合并js、合并图片。将浏览器一次访问需要的这些静态资源合并成一个文件,这样浏览器只需要一次请求即可。多张图片也可以合并成一张大图,如果每张图都有不同的超链接,可以通过css偏移响应鼠标点击操作,构造不同的URL;

    • 使用浏览器缓存:对于一个网站而言,css,js和一些图片这些静态资源更新的频率都比较低,而这些又是每次请求都需要加载的,如果把这些文件缓存在浏览器中,可以极好的改善性能,通过设置HTTP头中的Cache-Control和Expires的属性,可以设定浏览器缓存;

    • 启动压缩:在服务器端对文件进行压缩,浏览器端进行解压缩,可以有效的减少通信传输的数据量,但是压缩对服务器和浏览器产生一定的压力,在通信带宽良好而服务器资源不足的情况下要权衡考虑是否压缩;

    • css放在页面最上面,js放在页面最下面:浏览器会在下载完全部的css之后就对整个页面进行渲染,因此最好的做法是将css文件置于页面最上面,让其尽快加载。js则相反,浏览器加载js后立即执行,有可能会阻塞整个页面,所以最好将js置于页面最下面。但是要注意一点如果页面在解析时就需要用到的js,这时候放最下面就不合适了;

    • 减少cookie传输:cookie包含在每次请求和响应中,太大的cookie会严重影响数据传输,因此哪些数据需要写入cookie时需要慎重考虑,尽量减少cookie的数据量;此外对于一些静态资源的访问(css,js等),发送cookie并没有意义,可以考虑使用独立的域名访问,尽量避免请求静态资源时发送cookie,减少cookie的传输次数

  • 反向代理

    • 反向代理服务器位于机房一侧,代理网站web服务器接收http请求;

    • 反向代理可以起到保护网站安全的作用,所有来自互联网的请求都必须经过代理服务器,相当于在web服务器和可能的网络攻击之间建立了一个屏障;

    • 反向代理还可以通过配置缓存功能加速web请求,当用户第一次访问静态资源时,静态内容会被缓存到代理服务器上,这样当其他用户再次访问该静态资源时,就可以直接从反向代理服务器中获取,加快了web响应速度,减轻了web服务器的负载压力;当缓存的内容发生变化时,通过内部通知机制通知代理缓存失效,反向代理服务器会重新加载最新的内容再次缓存;

    • 反向代理也可以实现负载均衡的功能,通过负载均衡构建的应用集群可以提高系统的总体处理能力,进而改善网站高并发下的性能;

       

       

  • CDN加速

    • CDN又叫内容分发,本质上也是缓存,而且是将数据缓存在离用户最近的地方,当CDN中存在浏览器请求的资源时,直接从CDN返回给浏览器,最短路径返回响应,加快用户访问速度减少数据中心负载压力;

    • CDN能够缓存的一般是一些静态资源,如css,js,图片,静态网页等;

 

应用服务器性能优化

应用服务器就是处理网站业务的服务器,部署业务代码,是网站开发最复杂,变化最频繁的地方,优化手段主要体现在缓存集群异步和代码优化

  • 分布式缓存:回顾我们之前讲过的网站架构演变,当遇到性能瓶颈时,第一个想到的解决方案就是缓存,我们就好好讲下缓存;

    • 缓存的基本原理:缓存是指将数据存储在相对较高访问速度的存储介质中,以供调用。一方面缓存访问速度快,另一方面如果缓存的数据是经过计算处理得到的,那么被缓存的数据就无需在计算直接可以使用。

    • 缓存的本质:内存Hash表,数据缓存以一对kv的形式存储在内存hash表中,首先计算kv对中key的hashcode对应的hash表索引,可以快速访问hash表中的数据。然后通过hashcode计算hash表的索引下标,最简单的是余数法,使用hash表数组长度对hashcode求余,余数即为hash表索引,使用该索引可直接访问到hash表中存储的kv对,具体过程见下图:

      缓存主要用来存储一些读写比例较高,很少变化的数据,应用程序读取数据时,先到缓存中读取,如果读取不到或者数据已经失效,再访问数据库,并将数据写回缓存:

    • 合理使用缓存

      • 频繁修改数据:如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用来不及读取缓存,数据就已经失效的情形,反而增加了系统负担,一般来说数据读写比在2:1以上缓存才有意义;

      • 没有热点的访问:如果应用访问的数据没有热点,即缓存的数据很少被访问,那么缓存就没有意义,还没来得及被访问就已经被挤出缓存,这是无用的缓存;

      • 数据不一致与脏读:一般会对缓存数据设置失效时间,一旦超过失效时间,就要从数据库重新加载。因此应用要能接受一定时间的数据不一致;还有一种策略是数据更新时立即更新缓存,不过这种策略就会带来更多的系统开销和事务一致性问题;

      • 缓存可用性:缓存是为了提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的处理(可以直接从数据库拿),但是随着业务发展,缓存会承担大部分数据访问压力,所以当缓存服务崩溃时,就会对数据库造成突发的压力,而数据库很有可能不能承受如此大的压力而宕机,进而导致整个网站瘫痪,这种情况我们称之为缓存雪崩,发生这种情况甚至不能简单的重启缓存和数据库来恢复网站访问,所以我们就得考虑缓存的高可用性,通过分布式缓存服务器集群,将数据分布到多台服务器上,可在一定程度上预防雪崩,当一台服务器宕机的时候,只有部分数据丢失,重新从数据库加载这部分数据不会对数据库产生大的压力;

      • 缓存预热:缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰出来的,这个过程需要一定的时间开销、新启动的缓存如果没有任何数据,在重建缓存过程中,系统性能和数据库负载都不太好,那么最好在缓存系统重启的时候就把热点数据加载好,这个缓存预加载的方式叫缓存预热;

      • 缓存穿透:如果因为不恰当的业务或者恶意攻击持续高并发的请求某些不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成大的压力,甚至崩溃。这种情况叫缓存穿透或者说缓存不命中,一个简单的对策是将不存在的数据也缓存起来,对应v为null

    • 分布式缓存架构:现如今的分布式缓存架构有很多,例如redis集群,memcached集群,这里我就不一一说明了,感兴趣的可以自己去查阅相关资料,或者关注后期我们将缓存篇时会着重讲解;

  • 异步:使用消息队列将调用异步化,可以改善网站的扩展性和系统的性能

     

    在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得响应延迟加剧。

    使用消息队列之后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库,由于消息队列服务器处理速度远快于数据库,因此用户的响应延迟得到有效改善。

    消息队列还有一个作用——削峰即通过异步处理,将短时间内高并发产生的事务消息存储到消息队列中,从而消减了高峰期的并发事务;

  • 集群:在网站高并发访问的场景下,使用负载均衡为应用构建一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载压力过大而响应缓慢,如下图所示:

    三台服务器共同处理来自用户浏览器的访问请求,这样每台web服务器系统处理的请求只有总数的三分之一,根据性能测试曲线,使服务器的并发请求数目控制在最佳运行区间,活得最佳的访问请求延迟。

  • 代码优化:网站的业务逻辑实现代码主要部署在应用服务器上,需要处理复杂的并发事务。合理的优化代码,可以很好的改善网站性能。

    • 多线程:使用多线程的原因有2个,IO阻塞和多CPU,当前线程进行IO处理的时候,会被阻塞释放CPU以等待IO操作完成,由于IO操作通常都需要较长的时间,这时CPU可以调度其他的线程进行处理,所以要想最大限度的使用这些CPU就必须启动多线程;

      启动的线程数=任务执行时间/(任务执行时间-IO等待时间)*CPU内核数,最佳启动线程数和CPU内核数成正比,和IO阻塞时间成反比,如果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数,因为启动再多线程,CPU也来不及调度,相反如果任务是需要等待磁盘操作,网络响应,那么多启动线程就有助于提高任务并发度,提高系统吞吐量改善系统性能;

      多线程编程一个最重要的问题是线程安全问题,即多个线程并发对某个资源修改,导致数据混乱,一般解决线程安全问题主要考虑下面几点:

      将对象设计成无状态对象:就是对象本事不存储状态信息,这样多线程并发访问的时候就不会出现状态不一致的情况;

      使用局部对象:在方法的内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识将这些对象传递给其他线程,否则不会出现对象被多线程并发访问的情况;

      并发访问资源时使用锁:多个线程访问资源时,通过加锁的方式使多线程并发操作转化为顺序操作,从而避免资源被并发修改(关于这点我们后期会在并发编程中讨论具体细节);

    • 资源复用:系统运行时要尽量减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信,线程等,资源复用主要有2种模式:单例对象池

      对象池:通过复用对象实例,减少对象的创建和资源消耗

    • 数据结构:不同场景中合理使用恰当的数据结构,灵活组合各种数据结构改善数据读写和计算特性可极大优化程序的性能;

    • 垃圾回收:理解垃圾回收机制有助于程序优化和参数调优,以及编写内存安全的代码。

      以JVM为例,其内存主要分为堆和堆栈。堆栈用于存储线程上下文信息,如方法参数,局部变量等,堆则是存储对象的内存空间,对象的创建和释放、垃圾回收都在这里进行。通过对对象生命周期的观察,发现大部分对象的生命周期都极其短暂,这部分对象产生的垃圾应该更快地被收集以释放内存,这就是JVM的分代垃圾回收,具体过程如下:

      在JVM分代垃圾回收中,将应用程序可用的堆空间分为年轻代和年老代,又将年轻代分为Eden区、From和To区,新建对象总是在Eden区中被创建,当eden区空间已满,就触发一次YoungGC,将还被使用的对象复制到From区,这样整个Eden区都是未被使用的空间,可供继续创建对象,当Eden区再次被用完时,再触发一次YoungGC,将Eden区和From区中还在被使用的对象复制到To区,下一次YoungGC则是将Eden区和To区还在被使用的对象复制到From区,经过多次YoungGC,某些对象就会在From和To区之间多次复制,如果超过某个阈值对象还未被释放,则将该对象复制到OldGeneration区,如果OldGeneration空间也用完了那么就会触发FullGC,即所谓的全量回收,全量回收会对系统性能造成较大影响,因此应该根据业务特点和对象生命周期合理的设置YoungGeneration和OldGeneration的大小,尽量减少FullGC;

 

存储性能优化

 

性能测试指标

 

性能测试方法

 

 

 

性能优化策略

如果性能测试的结果不能满足业务需求,或者说不是很令人满意,那么就要进行性能分析和优化了

 

 

web前端性能优化

一般来说web前端指的是网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、cdn服务等,优化手段主要体现在浏览器访问、反向代理和CDN等几个方面:

 

应用服务器就是处理网站业务的服务器,部署业务代码,是网站开发最复杂,变化最频繁的地方,优化手段主要体现在缓存集群异步和代码优化

 

 

存储性能优化

 

性能测试指标

 

性能测试方法

 

 

 

性能优化策略

如果性能测试的结果不能满足业务需求,或者说不是很令人满意,那么就要进行性能分析和优化了

 

 

web前端性能优化

一般来说web前端指的是网站业务逻辑之前的部分,包括浏览器加载、网站视图模型、图片服务、cdn服务等,优化手段主要体现在浏览器访问、反向代理和CDN等几个方面:

 

应用服务器就是处理网站业务的服务器,部署业务代码,是网站开发最复杂,变化最频繁的地方,优化手段主要体现在缓存集群异步和代码优化

 

 

存储性能优化

 

总结

综合权衡:性能提升一倍,但是服务器需要增加一倍;或者响应时间缩短,同时数据一致性也下降,这样的优化是否可以接受?

归根结底,技术是为业务服务的,技术选型和架构决策依赖业务规划乃至企业战略规划,离开业务发展的支撑和驱动,技术走不远,甚至会迷失方向;

 

注:因侧重点关系,本文某些知识点只是很少的一笔带过,感兴趣的可以自己查阅相关资料,或者后期我可能会在其他文章中详细讲解

 

 

应用服务器性能优化

 

总结

综合权衡:性能提升一倍,但是服务器需要增加一倍;或者响应时间缩短,同时数据一致性也下降,这样的优化是否可以接受?

归根结底,技术是为业务服务的,技术选型和架构决策依赖业务规划乃至企业战略规划,离开业务发展的支撑和驱动,技术走不远,甚至会迷失方向;

 

注:因侧重点关系,本文某些知识点只是很少的一笔带过,感兴趣的可以自己查阅相关资料,或者后期我可能会在其他文章中详细讲解

 

 

应用服务器性能优化

  • 机械硬盘和固态硬盘

  • B+树和LSM树:B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为单位存储在磁盘中,从根开始查找所需数据所在节点编号和磁盘位置,将其加载到内存中然后继续查找,直到找到所需的数据;LSM可以看作是一个N阶合并树,数据的写操作都在内存中完成,每进行一次数据更新不需要磁盘访问,速度远快于B+树,当数据访问以写操作为主,而读操作则集中在最近写入的数据上时,使用LSM树可以极大程度的减少磁盘的访问次数,加快访问速度;

  • RAID和HDFS:廉价磁盘冗余阵列(RAID)主要是为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力;Hadoop分布式文件系统(HDFS)中系统在整个存储集群的多台服务器上进行数据并发读写和备份(HDFS此处不做详细介绍);

    前言

    上节我们讲了架构设计的五大核心要素,今天我们就讲第一个核心要素:性能,性能是一个网站给用户最直接的感受,是一个网站的灵魂。下面我们将从以下几个方面谈谈如何构建一个高性能架构:网站性能测试web前端优化应用服务器优化存储性能优化

     

    网站性能测试

    性能测试是性能优化的前提和基础,也是性能优化结果的检验和度量标准,不同视角下的网站有不同的标准,也对应不同的优化。下面我们将依次展开:

    不同视角下的网站性能

  • 用户视角:对于用户而言,发出一个请求,到响应结束所用的时间就是最直观的性能表现,如下图就是一个用户对性能最直观的体现方式

  •         主要包括计算机和网站服务器通信的时间,网站服务器处理请求的时间,

            计算机浏览器构造请求解析和响应的时间,主要优化手段是使用一些前端

            架构优化方案,例如优化HTML样式,利用浏览器的缓存和异步特性调整

            浏览器缓存策略,使用cdn服务和反向代理手段等;

  • 开发人员:开发人员主要关注的是应用程序本身及其相关子系统的性能,主要包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标;主要的优化手段有使用缓存加速数据读取,使用集群提高吞吐量,使用异步消息加快请求响应及实现削峰,使用代码优化程序自身;

  • 运维人员:运维人员主要关注基础设施性能和资源的利用率,主要包括网络运营商的带宽,服务器硬件的配置,数据中心网络架构,服务器和网络带宽的利用率等;主要的优化手段有建设优化骨干网络,使用高性价比定制服务器配置,利用虚拟化技术优化资源利用;

  • 响应时间:指应用执行一个操作需要的时间,包括从请求开始到请求最后响应数据所需的所有时间。响应时间是系统最重要的性能指标,直观的反映了系统的“快慢”;

  • 并发数:指系统能够同时处理请求的数目,这个数量也反映了系统负载的特性,对于网站而言,并发数就是网站并发用户数,即同时提交请求的用户数量。与网站并发数对应的还有网站系统用户数(可以访问系统的总用户数,一般为系统的注册用户数)和网站在线用户数(当前登录系统的用户总数),这三者的关系:网站系统用户数>>网站在线用户数>>网站并发用户数;

  • 吞吐量:指单位时间内处理的请求数量,体现系统的整体处理能力。主要通过TPS(每秒事务数)、HPS(每秒HTTP请求数)和QPS(每秒查询数)来体现

  • 性能计数器:指描述服务器或者操作系统性能的一些数据指标,主要包括系统负载(System Load)、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等;

    • 系统负载:指当前正在被CPU执行和等待被CPU执行的进程数总和,是反映系统忙闲程度的重要指标。多核CPU情况下,最理想的是所有CPU都在使用,没有进程在等待被处理,此时load的理想值就是CPU的数目;当load<cpu数时,表示CPU有空闲,存在资源浪费;当load>cpu数时,表示有进程在排队等待CPU调度,系统资源不足,影响应用程序的执行性能,Linux中使用top查看系统负载。

  • 性能测试:系统设计初期规划的性能指标为预期目标,在对系统不断施加压力,验证系统在资源可接受范围内是否能达到性能预期目标;

  • 负载测试:对系统不断增加并发请求以增加系统压力,直到系统的某项性能达到安全临界值,如果某个资源已经饱和状态时,继续对系统施加压力的话,系统的处理能力不但不能提升,反而会下降;

  • 压力测试:当系统超过安全负载的时候,对系统继续施加压力,直到系统崩溃或者不能再处理任何请求,以此来获取系统最大压力承受能力;

  • 稳定性测试:被测试的系统在特定的硬件、软件、网络环境下,给系统加载一定业务压力,使系统运行一段较长时间,以此来检测系统是否稳定;

  • 性能分析:排查网站性能和程序性能瓶颈的基本方法是,检查请求处理的各个环节的日志,分析哪个环节响应时间不合理,超过了预期;然后检查监控数据,分析影响性能的主要因素是内存、磁盘、网络还是CPU,是代码问题还是架构不合理,或者系统资源确实不足;

  • 性能优化:根据网站分层架构,可以把性能优化也分为web前端性能优化、应用服务器性能优化、存储服务器性能优化;

  • 浏览器访问优化

    • 减少http请求:HTTP协议都是无状态的应用层协议,意味着每次http请求都需要建立通信链路,进行数据传输,而在服务端,每个http请求都需要开启一个独立的线程去处理,增加了服务器的开销,所以减少HTTP请求的数目可以有效的提高访问性能;减少http请求的主要手段包括合并css、合并js、合并图片。将浏览器一次访问需要的这些静态资源合并成一个文件,这样浏览器只需要一次请求即可。多张图片也可以合并成一张大图,如果每张图都有不同的超链接,可以通过css偏移响应鼠标点击操作,构造不同的URL;

    • 使用浏览器缓存:对于一个网站而言,css,js和一些图片这些静态资源更新的频率都比较低,而这些又是每次请求都需要加载的,如果把这些文件缓存在浏览器中,可以极好的改善性能,通过设置HTTP头中的Cache-Control和Expires的属性,可以设定浏览器缓存;

    • 启动压缩:在服务器端对文件进行压缩,浏览器端进行解压缩,可以有效的减少通信传输的数据量,但是压缩对服务器和浏览器产生一定的压力,在通信带宽良好而服务器资源不足的情况下要权衡考虑是否压缩;

    • css放在页面最上面,js放在页面最下面:浏览器会在下载完全部的css之后就对整个页面进行渲染,因此最好的做法是将css文件置于页面最上面,让其尽快加载。js则相反,浏览器加载js后立即执行,有可能会阻塞整个页面,所以最好将js置于页面最下面。但是要注意一点如果页面在解析时就需要用到的js,这时候放最下面就不合适了;

    • 减少cookie传输:cookie包含在每次请求和响应中,太大的cookie会严重影响数据传输,因此哪些数据需要写入cookie时需要慎重考虑,尽量减少cookie的数据量;此外对于一些静态资源的访问(css,js等),发送cookie并没有意义,可以考虑使用独立的域名访问,尽量避免请求静态资源时发送cookie,减少cookie的传输次数

  • 反向代理

    • 反向代理服务器位于机房一侧,代理网站web服务器接收http请求;

    • 反向代理可以起到保护网站安全的作用,所有来自互联网的请求都必须经过代理服务器,相当于在web服务器和可能的网络攻击之间建立了一个屏障;

    • 反向代理还可以通过配置缓存功能加速web请求,当用户第一次访问静态资源时,静态内容会被缓存到代理服务器上,这样当其他用户再次访问该静态资源时,就可以直接从反向代理服务器中获取,加快了web响应速度,减轻了web服务器的负载压力;当缓存的内容发生变化时,通过内部通知机制通知代理缓存失效,反向代理服务器会重新加载最新的内容再次缓存;

    • 反向代理也可以实现负载均衡的功能,通过负载均衡构建的应用集群可以提高系统的总体处理能力,进而改善网站高并发下的性能;

       

       

  • CDN加速

    • CDN又叫内容分发,本质上也是缓存,而且是将数据缓存在离用户最近的地方,当CDN中存在浏览器请求的资源时,直接从CDN返回给浏览器,最短路径返回响应,加快用户访问速度减少数据中心负载压力;

    • CDN能够缓存的一般是一些静态资源,如css,js,图片,静态网页等;

  • 分布式缓存:回顾我们之前讲过的网站架构演变,当遇到性能瓶颈时,第一个想到的解决方案就是缓存,我们就好好讲下缓存;

    • 缓存的基本原理:缓存是指将数据存储在相对较高访问速度的存储介质中,以供调用。一方面缓存访问速度快,另一方面如果缓存的数据是经过计算处理得到的,那么被缓存的数据就无需在计算直接可以使用。

    • 缓存的本质:内存Hash表,数据缓存以一对kv的形式存储在内存hash表中,首先计算kv对中key的hashcode对应的hash表索引,可以快速访问hash表中的数据。然后通过hashcode计算hash表的索引下标,最简单的是余数法,使用hash表数组长度对hashcode求余,余数即为hash表索引,使用该索引可直接访问到hash表中存储的kv对,具体过程见下图:

      缓存主要用来存储一些读写比例较高,很少变化的数据,应用程序读取数据时,先到缓存中读取,如果读取不到或者数据已经失效,再访问数据库,并将数据写回缓存:

    • 合理使用缓存

      • 频繁修改数据:如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用来不及读取缓存,数据就已经失效的情形,反而增加了系统负担,一般来说数据读写比在2:1以上缓存才有意义;

      • 没有热点的访问:如果应用访问的数据没有热点,即缓存的数据很少被访问,那么缓存就没有意义,还没来得及被访问就已经被挤出缓存,这是无用的缓存;

      • 数据不一致与脏读:一般会对缓存数据设置失效时间,一旦超过失效时间,就要从数据库重新加载。因此应用要能接受一定时间的数据不一致;还有一种策略是数据更新时立即更新缓存,不过这种策略就会带来更多的系统开销和事务一致性问题;

      • 缓存可用性:缓存是为了提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的处理(可以直接从数据库拿),但是随着业务发展,缓存会承担大部分数据访问压力,所以当缓存服务崩溃时,就会对数据库造成突发的压力,而数据库很有可能不能承受如此大的压力而宕机,进而导致整个网站瘫痪,这种情况我们称之为缓存雪崩,发生这种情况甚至不能简单的重启缓存和数据库来恢复网站访问,所以我们就得考虑缓存的高可用性,通过分布式缓存服务器集群,将数据分布到多台服务器上,可在一定程度上预防雪崩,当一台服务器宕机的时候,只有部分数据丢失,重新从数据库加载这部分数据不会对数据库产生大的压力;

      • 缓存预热:缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰出来的,这个过程需要一定的时间开销、新启动的缓存如果没有任何数据,在重建缓存过程中,系统性能和数据库负载都不太好,那么最好在缓存系统重启的时候就把热点数据加载好,这个缓存预加载的方式叫缓存预热;

      • 缓存穿透:如果因为不恰当的业务或者恶意攻击持续高并发的请求某些不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成大的压力,甚至崩溃。这种情况叫缓存穿透或者说缓存不命中,一个简单的对策是将不存在的数据也缓存起来,对应v为null

    • 分布式缓存架构:现如今的分布式缓存架构有很多,例如redis集群,memcached集群,这里我就不一一说明了,感兴趣的可以自己去查阅相关资料,或者关注后期我们将缓存篇时会着重讲解;

  • 异步:使用消息队列将调用异步化,可以改善网站的扩展性和系统的性能

     

    在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得响应延迟加剧。

    使用消息队列之后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库,由于消息队列服务器处理速度远快于数据库,因此用户的响应延迟得到有效改善。

    消息队列还有一个作用——削峰即通过异步处理,将短时间内高并发产生的事务消息存储到消息队列中,从而消减了高峰期的并发事务;

  • 集群:在网站高并发访问的场景下,使用负载均衡为应用构建一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载压力过大而响应缓慢,如下图所示:

    三台服务器共同处理来自用户浏览器的访问请求,这样每台web服务器系统处理的请求只有总数的三分之一,根据性能测试曲线,使服务器的并发请求数目控制在最佳运行区间,活得最佳的访问请求延迟。

  • 代码优化:网站的业务逻辑实现代码主要部署在应用服务器上,需要处理复杂的并发事务。合理的优化代码,可以很好的改善网站性能。

    • 多线程:使用多线程的原因有2个,IO阻塞和多CPU,当前线程进行IO处理的时候,会被阻塞释放CPU以等待IO操作完成,由于IO操作通常都需要较长的时间,这时CPU可以调度其他的线程进行处理,所以要想最大限度的使用这些CPU就必须启动多线程;

      启动的线程数=任务执行时间/(任务执行时间-IO等待时间)*CPU内核数,最佳启动线程数和CPU内核数成正比,和IO阻塞时间成反比,如果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数,因为启动再多线程,CPU也来不及调度,相反如果任务是需要等待磁盘操作,网络响应,那么多启动线程就有助于提高任务并发度,提高系统吞吐量改善系统性能;

      多线程编程一个最重要的问题是线程安全问题,即多个线程并发对某个资源修改,导致数据混乱,一般解决线程安全问题主要考虑下面几点:

      将对象设计成无状态对象:就是对象本事不存储状态信息,这样多线程并发访问的时候就不会出现状态不一致的情况;

      使用局部对象:在方法的内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识将这些对象传递给其他线程,否则不会出现对象被多线程并发访问的情况;

      并发访问资源时使用锁:多个线程访问资源时,通过加锁的方式使多线程并发操作转化为顺序操作,从而避免资源被并发修改(关于这点我们后期会在并发编程中讨论具体细节);

    • 资源复用:系统运行时要尽量减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信,线程等,资源复用主要有2种模式:单例对象池

      对象池:通过复用对象实例,减少对象的创建和资源消耗

    • 数据结构:不同场景中合理使用恰当的数据结构,灵活组合各种数据结构改善数据读写和计算特性可极大优化程序的性能;

    • 垃圾回收:理解垃圾回收机制有助于程序优化和参数调优,以及编写内存安全的代码。

      以JVM为例,其内存主要分为堆和堆栈。堆栈用于存储线程上下文信息,如方法参数,局部变量等,堆则是存储对象的内存空间,对象的创建和释放、垃圾回收都在这里进行。通过对对象生命周期的观察,发现大部分对象的生命周期都极其短暂,这部分对象产生的垃圾应该更快地被收集以释放内存,这就是JVM的分代垃圾回收,具体过程如下:

      在JVM分代垃圾回收中,将应用程序可用的堆空间分为年轻代和年老代,又将年轻代分为Eden区、From和To区,新建对象总是在Eden区中被创建,当eden区空间已满,就触发一次YoungGC,将还被使用的对象复制到From区,这样整个Eden区都是未被使用的空间,可供继续创建对象,当Eden区再次被用完时,再触发一次YoungGC,将Eden区和From区中还在被使用的对象复制到To区,下一次YoungGC则是将Eden区和To区还在被使用的对象复制到From区,经过多次YoungGC,某些对象就会在From和To区之间多次复制,如果超过某个阈值对象还未被释放,则将该对象复制到OldGeneration区,如果OldGeneration空间也用完了那么就会触发FullGC,即所谓的全量回收,全量回收会对系统性能造成较大影响,因此应该根据业务特点和对象生命周期合理的设置YoungGeneration和OldGeneration的大小,尽量减少FullGC;

  • 机械硬盘和固态硬盘

  • B+树和LSM树:B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为单位存储在磁盘中,从根开始查找所需数据所在节点编号和磁盘位置,将其加载到内存中然后继续查找,直到找到所需的数据;LSM可以看作是一个N阶合并树,数据的写操

    前言

    上节我们讲了架构设计的五大核心要素,今天我们就讲第一个核心要素:性能,性能是一个网站给用户最直接的感受,是一个网站的灵魂。下面我们将从以下几个方面谈谈如何构建一个高性能架构:网站性能测试web前端优化应用服务器优化存储性能优化

     

    网站性能测试

    性能测试是性能优化的前提和基础,也是性能优化结果的检验和度量标准,不同视角下的网站有不同的标准,也对应不同的优化。下面我们将依次展开:

    不同视角下的网站性能

  • 用户视角:对于用户而言,发出一个请求,到响应结束所用的时间就是最直观的性能表现,如下图就是一个用户对性能最直观的体现方式

  •         主要包括计算机和网站服务器通信的时间,网站服务器处理请求的时间,

            计算机浏览器构造请求解析和响应的时间,主要优化手段是使用一些前端

            架构优化方案,例如优化HTML样式,利用浏览器的缓存和异步特性调整

            浏览器缓存策略,使用cdn服务和反向代理手段等;

  • 开发人员:开发人员主要关注的是应用程序本身及其相关子系统的性能,主要包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标;主要的优化手段有使用缓存加速数据读取,使用集群提高吞吐量,使用异步消息加快请求响应及实现削峰,使用代码优化程序自身;

  • 运维人员:运维人员主要关注基础设施性能和资源的利用率,主要包括网络运营商的带宽,服务器硬件的配置,数据中心网络架构,服务器和网络带宽的利用率等;主要的优化手段有建设优化骨干网络,使用高性价比定制服务器配置,利用虚拟化技术优化资源利用;

  • 响应时间:指应用执行一个操作需要的时间,包括从请求开始到请求最后响应数据所需的所有时间。响应时间是系统最重要的性能指标,直观的反映了系统的“快慢”;

  • 并发数:指系统能够同时处理请求的数目,这个数量也反映了系统负载的特性,对于网站而言,并发数就是网站并发用户数,即同时提交请求的用户数量。与网站并发数对应的还有网站系统用户数(可以访问系统的总用户数,一般为系统的注册用户数)和网站在线用户数(当前登录系统的用户总数),这三者的关系:网站系统用户数>>网站在线用户数>>网站并发用户数;

  • 吞吐量:指单位时间内处理的请求数量,体现系统的整体处理能力。主要通过TPS(每秒事务数)、HPS(每秒HTTP请求数)和QPS(每秒查询数)来体现

  • 性能计数器:指描述服务器或者操作系统性能的一些数据指标,主要包括系统负载(System Load)、对象与线程数、内存使用、CPU使用、磁盘与网络I/O等;

    • 系统负载:指当前正在被CPU执行和等待被CPU执行的进程数总和,是反映系统忙闲程度的重要指标。多核CPU情况下,最理想的是所有CPU都在使用,没有进程在等待被处理,此时load的理想值就是CPU的数目;当load<cpu数时,表示CPU有空闲,存在资源浪费;当load>cpu数时,表示有进程在排队等待CPU调度,系统资源不足,影响应用程序的执行性能,Linux中使用top查看系统负载。

  • 性能测试:系统设计初期规划的性能指标为预期目标,在对系统不断施加压力,验证系统在资源可接受范围内是否能达到性能预期目标;

  • 负载测试:对系统不断增加并发请求以增加系统压力,直到系统的某项性能达到安全临界值,如果某个资源已经饱和状态时,继续对系统施加压力的话,系统的处理能力不但不能提升,反而会下降;

  • 压力测试:当系统超过安全负载的时候,对系统继续施加压力,直到系统崩溃或者不能再处理任何请求,以此来获取系统最大压力承受能力;

  • 稳定性测试:被测试的系统在特定的硬件、软件、网络环境下,给系统加载一定业务压力,使系统运行一段较长时间,以此来检测系统是否稳定;

  • 性能分析:排查网站性能和程序性能瓶颈的基本方法是,检查请求处理的各个环节的日志,分析哪个环节响应时间不合理,超过了预期;然后检查监控数据,分析影响性能的主要因素是内存、磁盘、网络还是CPU,是代码问题还是架构不合理,或者系统资源确实不足;

  • 性能优化:根据网站分层架构,可以把性能优化也分为web前端性能优化、应用服务器性能优化、存储服务器性能优化;

  • 浏览器访问优化

    • 减少http请求:HTTP协议都是无状态的应用层协议,意味着每次http请求都需要建立通信链路,进行数据传输,而在服务端,每个http请求都需要开启一个独立的线程去处理,增加了服务器的开销,所以减少HTTP请求的数目可以有效的提高访问性能;减少http请求的主要手段包括合并css、合并js、合并图片。将浏览器一次访问需要的这些静态资源合并成一个文件,这样浏览器只需要一次请求即可。多张图片也可以合并成一张大图,如果每张图都有不同的超链接,可以通过css偏移响应鼠标点击操作,构造不同的URL;

    • 使用浏览器缓存:对于一个网站而言,css,js和一些图片这些静态资源更新的频率都比较低,而这些又是每次请求都需要加载的,如果把这些文件缓存在浏览器中,可以极好的改善性能,通过设置HTTP头中的Cache-Control和Expires的属性,可以设定浏览器缓存;

    • 启动压缩:在服务器端对文件进行压缩,浏览器端进行解压缩,可以有效的减少通信传输的数据量,但是压缩对服务器和浏览器产生一定的压力,在通信带宽良好而服务器资源不足的情况下要权衡考虑是否压缩;

    • css放在页面最上面,js放在页面最下面:浏览器会在下载完全部的css之后就对整个页面进行渲染,因此最好的做法是将css文件置于页面最上面,让其尽快加载。js则相反,浏览器加载js后立即执行,有可能会阻塞整个页面,所以最好将js置于页面最下面。但是要注意一点如果页面在解析时就需要用到的js,这时候放最下面就不合适了;

    • 减少cookie传输:cookie包含在每次请求和响应中,太大的cookie会严重影响数据传输,因此哪些数据需要写入cookie时需要慎重考虑,尽量减少cookie的数据量;此外对于一些静态资源的访问(css,js等),发送cookie并没有意义,可以考虑使用独立的域名访问,尽量避免请求静态资源时发送cookie,减少cookie的传输次数

  • 反向代理

    • 反向代理服务器位于机房一侧,代理网站web服务器接收http请求;

    • 反向代理可以起到保护网站安全的作用,所有来自互联网的请求都必须经过代理服务器,相当于在web服务器和可能的网络攻击之间建立了一个屏障;

    • 反向代理还可以通过配置缓存功能加速web请求,当用户第一次访问静态资源时,静态内容会被缓存到代理服务器上,这样当其他用户再次访问该静态资源时,就可以直接从反向代理服务器中获取,加快了web响应速度,减轻了web服务器的负载压力;当缓存的内容发生变化时,通过内部通知机制通知代理缓存失效,反向代理服务器会重新加载最新的内容再次缓存;

    • 反向代理也可以实现负载均衡的功能,通过负载均衡构建的应用集群可以提高系统的总体处理能力,进而改善网站高并发下的性能;

       

       

  • CDN加速

    • CDN又叫内容分发,本质上也是缓存,而且是将数据缓存在离用户最近的地方,当CDN中存在浏览器请求的资源时,直接从CDN返回给浏览器,最短路径返回响应,加快用户访问速度减少数据中心负载压力;

    • CDN能够缓存的一般是一些静态资源,如css,js,图片,静态网页等;

  • 分布式缓存:回顾我们之前讲过的网站架构演变,当遇到性能瓶颈时,第一个想到的解决方案就是缓存,我们就好好讲下缓存;

    • 缓存的基本原理:缓存是指将数据存储在相对较高访问速度的存储介质中,以供调用。一方面缓存访问速度快,另一方面如果缓存的数据是经过计算处理得到的,那么被缓存的数据就无需在计算直接可以使用。

    • 缓存的本质:内存Hash表,数据缓存以一对kv的形式存储在内存hash表中,首先计算kv对中key的hashcode对应的hash表索引,可以快速访问hash表中的数据。然后通过hashcode计算hash表的索引下标,最简单的是余数法,使用hash表数组长度对hashcode求余,余数即为hash表索引,使用该索引可直接访问到hash表中存储的kv对,具体过程见下图:

      缓存主要用来存储一些读写比例较高,很少变化的数据,应用程序读取数据时,先到缓存中读取,如果读取不到或者数据已经失效,再访问数据库,并将数据写回缓存:

    • 合理使用缓存

      • 频繁修改数据:如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,应用来不及读取缓存,数据就已经失效的情形,反而增加了系统负担,一般来说数据读写比在2:1以上缓存才有意义;

      • 没有热点的访问:如果应用访问的数据没有热点,即缓存的数据很少被访问,那么缓存就没有意义,还没来得及被访问就已经被挤出缓存,这是无用的缓存;

      • 数据不一致与脏读:一般会对缓存数据设置失效时间,一旦超过失效时间,就要从数据库重新加载。因此应用要能接受一定时间的数据不一致;还有一种策略是数据更新时立即更新缓存,不过这种策略就会带来更多的系统开销和事务一致性问题;

      • 缓存可用性:缓存是为了提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的处理(可以直接从数据库拿),但是随着业务发展,缓存会承担大部分数据访问压力,所以当缓存服务崩溃时,就会对数据库造成突发的压力,而数据库很有可能不能承受如此大的压力而宕机,进而导致整个网站瘫痪,这种情况我们称之为缓存雪崩,发生这种情况甚至不能简单的重启缓存和数据库来恢复网站访问,所以我们就得考虑缓存的高可用性,通过分布式缓存服务器集群,将数据分布到多台服务器上,可在一定程度上预防雪崩,当一台服务器宕机的时候,只有部分数据丢失,重新从数据库加载这部分数据不会对数据库产生大的压力;

      • 缓存预热:缓存中存放的是热点数据,热点数据又是缓存系统利用LRU(最近最久未用算法)对不断访问的数据筛选淘汰出来的,这个过程需要一定的时间开销、新启动的缓存如果没有任何数据,在重建缓存过程中,系统性能和数据库负载都不太好,那么最好在缓存系统重启的时候就把热点数据加载好,这个缓存预加载的方式叫缓存预热;

      • 缓存穿透:如果因为不恰当的业务或者恶意攻击持续高并发的请求某些不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成大的压力,甚至崩溃。这种情况叫缓存穿透或者说缓存不命中,一个简单的对策是将不存在的数据也缓存起来,对应v为null

    • 分布式缓存架构:现如今的分布式缓存架构有很多,例如redis集群,memcached集群,这里我就不一一说明了,感兴趣的可以自己去查阅相关资料,或者关注后期我们将缓存篇时会着重讲解;

  • 异步:使用消息队列将调用异步化,可以改善网站的扩展性和系统的性能

     

    在不使用消息队列的情况下,用户的请求数据直接写入数据库,在高并发的情况下,会对数据库造成巨大的压力,同时也使得响应延迟加剧。

    使用消息队列之后,用户请求的数据发送给消息队列后立即返回,再由消息队列的消费者进程从消息队列中获取数据,异步写入数据库,由于消息队列服务器处理速度远快于数据库,因此用户的响应延迟得到有效改善。

    消息队列还有一个作用——削峰即通过异步处理,将短时间内高并发产生的事务消息存储到消息队列中,从而消减了高峰期的并发事务;

  • 集群:在网站高并发访问的场景下,使用负载均衡为应用构建一个由多台服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理,避免单一服务器因负载压力过大而响应缓慢,如下图所示:

    三台服务器共同处理来自用户浏览器的访问请求,这样每台web服务器系统处理的请求只有总数的三分之一,根据性能测试曲线,使服务器的并发请求数目控制在最佳运行区间,活得最佳的访问请求延迟。

  • 代码优化:网站的业务逻辑实现代码主要部署在应用服务器上,需要处理复杂的并发事务。合理的优化代码,可以很好的改善网站性能。

    • 多线程:使用多线程的原因有2个,IO阻塞和多CPU,当前线程进行IO处理的时候,会被阻塞释放CPU以等待IO操作完成,由于IO操作通常都需要较长的时间,这时CPU可以调度其他的线程进行处理,所以要想最大限度的使用这些CPU就必须启动多线程;

      启动的线程数=任务执行时间/(任务执行时间-IO等待时间)*CPU内核数,最佳启动线程数和CPU内核数成正比,和IO阻塞时间成反比,如果任务都是CPU计算型任务,那么线程数最多不超过CPU内核数,因为启动再多线程,CPU也来不及调度,相反如果任务是需要等待磁盘操作,网络响应,那么多启动线程就有助于提高任务并发度,提高系统吞吐量改善系统性能;

      多线程编程一个最重要的问题是线程安全问题,即多个线程并发对某个资源修改,导致数据混乱,一般解决线程安全问题主要考虑下面几点:

      将对象设计成无状态对象:就是对象本事不存储状态信息,这样多线程并发访问的时候就不会出现状态不一致的情况;

      使用局部对象:在方法的内部创建对象,这些对象会被每个进入该方法的线程创建,除非程序有意识将这些对象传递给其他线程,否则不会出现对象被多线程并发访问的情况;

      并发访问资源时使用锁:多个线程访问资源时,通过加锁的方式使多线程并发操作转化为顺序操作,从而避免资源被并发修改(关于这点我们后期会在并发编程中讨论具体细节);

    • 资源复用:系统运行时要尽量减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信,线程等,资源复用主要有2种模式:单例对象池

      对象池:通过复用对象实例,减少对象的创建和资源消耗

    • 数据结构:不同场景中合理使用恰当的数据结构,灵活组合各种数据结构改善数据读写和计算特性可极大优化程序的性能;

    • 垃圾回收:理解垃圾回收机制有助于程序优化和参数调优,以及编写内存安全的代码。

      以JVM为例,其内存主要分为堆和堆栈。堆栈用于存储线程上下文信息,如方法参数,局部变量等,堆则是存储对象的内存空间,对象的创建和释放、垃圾回收都在这里进行。通过对对象生命周期的观察,发现大部分对象的生命周期都极其短暂,这部分对象产生的垃圾应该更快地被收集以释放内存,这就是JVM的分代垃圾回收,具体过程如下:

      在JVM分代垃圾回收中,将应用程序可用的堆空间分为年轻代和年老代,又将年轻代分为Eden区、From和To区,新建对象总是在Eden区中被创建,当eden区空间已满,就触发一次YoungGC,将还被使用的对象复制到From区,这样整个Eden区都是未被使用的空间,可供继续创建对象,当Eden区再次被用完时,再触发一次YoungGC,将Eden区和From区中还在被使用的对象复制到To区,下一次YoungGC则是将Eden区和To区还在被使用的对象复制到From区,经过多次YoungGC,某些对象就会在From和To区之间多次复制,如果超过某个阈值对象还未被释放,则将该对象复制到OldGeneration区,如果OldGeneration空间也用完了那么就会触发FullGC,即所谓的全量回收,全量回收会对系统性能造成较大影响,因此应该根据业务特点和对象生命周期合理的设置YoungGeneration和OldGeneration的大小,尽量减少FullGC;

  • 机械硬盘和固态硬盘

  • B+树和LSM树:B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为单位存储在磁盘中,从根开始查找所需数据所在节点编号和磁盘位置,将其加载到内存中然后继续查找,直到找到所需的数据;LSM可以看作是一个N阶合并树,数据的写操作都在内存中完成,每进行一次数据更新不需要磁盘访问,速度远快于B+树,当数据访问以写操作为主,而读操作则集中在最近写入的数据上时,使用LSM树可以极大程度的减少磁盘的访问次数,加快访问速度;

  • RAID和HDFS:廉价磁盘冗余阵列(RAID)主要是为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力;Hadoop分布式文件系统(HDFS)中系统在整个存储集群的多台服务器上进行数据并发读写和备份(HDFS此处不做详细介绍);

     

  • 作都在内存中完成,每进行一次数据更新不需要磁盘访问,速度远快于B+树,当数据访问以写操作为主,而读操作则集中在最近写入的数据上时,使用LSM树可以极大程度的减少磁盘的访问次数,加快访问速度;

  • RAID和HDFS:廉价磁盘冗余阵列(RAID)主要是为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力;Hadoop分布式文件系统(HDFS)中系统在整个存储集群的多台服务器上进行数据并发读写和备份(HDFS此处不做详细介绍);

     

总结

综合权衡:性能提升一倍,但是服务器需要增加一倍;或者响应时间缩短,同时数据一致性也下降,这样的优化是否可以接受?

归根结底,技术是为业务服务的,技术选型和架构决策依赖业务规划乃至企业战略规划,离开业务发展的支撑和驱动,技术走不远,甚至会迷失方向;

 

注:因侧重点关系,本文某些知识点只是很少的一笔带过,感兴趣的可以自己查阅相关资料,或者后期我可能会在其他文章中详细讲解

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值