1000字范文,内容丰富有趣,学习的好帮手!
1000字范文 > 五 系统架构 - 高性能架构设计及性能优化

五 系统架构 - 高性能架构设计及性能优化

时间:2023-01-18 13:26:18

相关推荐

五 系统架构 - 高性能架构设计及性能优化

目录

性能测试

不同视角下的网站性能

性能指标

性能测试

性能测试报告

性能优化策略

前端性能优化

开启缓存

启用压缩

改代码

后端性能优化

分布式缓存

异步操作(消息队列)

使用集群

代码优化

SQL性能优化

存储性能优化

使用SSD替代HDD

B+数 vs LSM树

RAID vs HDFS

大型网站中的一个重要要素,有人说性能就是访问速度的快慢,也是用户的真实体验。用户从输入网站到按下回车键,看到页面的快慢,这就是性能。

性能测试

性能测试是性能优化的前提和基础,也是性能优化结果的检查和度量标准。

不同视角下的网站性能

用户视角:关注网站打开时间

开发视角:关注应用程序的性能,如响应时间、吞吐量、并发处理能力

运维视角:关注基础设施性能和资源利用率,如带宽、服务器硬件配置

性能指标

响应时间:指应用执行一个操作所需的时间,从发出请求到最后得到响应的时间。=> 直观反映系统的快慢

并发数:指系统能够同时处理请求的数量 => 反映系统的负载。

网站用户数 >= 在线用户数 >= 并发用户数

吞吐量:指单位时间内系统处理的请求数量 => 体现系统整体处理能力

每秒请求数、TPS(每秒事务数)、HPS(每秒HTTP请求数)、QPS(每秒查询数)、每秒页面数、每天访问量、每小时处理业务量、

性能计数器:描述服务器或OS性能的数据指标

包括系统负载、对象与线程数、内存使用、CPU使用、网络与磁盘IO等指标

性能测试

性能测试是一个总称,其通过对系统不断添加访问压力,来获得系统性能指标、最大负载能力、最大压力承受能力的过程。具体细分如下四类:

性能测试:初期是性能测试,查看系统性能指标是否符合要求;负载测试:继续加压,是负载测试,查看系统的负载能力; 压力测试:继续加压,是压力测试,查看系统最大压力承受能力;继续加压,系统就凉凉;稳定性测试:在特定软硬件、网络环境下,给系统添加一定压力,使其运行一段时间,以检测系统是否稳定

响应时间与并发用户数的关系:

性能测试报告

给出并发数、响应时间、TPS(每秒事务数)、错误率、资源占用情况:

性能优化策略

性能分析(看日志、查监控、看代码、看SQL) =》性能优化(前端、后端、存储)

前端性能优化

先缓存、再压缩、最后改代码

开启缓存

浏览器缓存

利用HTTP协议的Cache-Control(缓存控制策略)和Expires(设置失效时间)头来控制缓存,这样在浏览器缓存后,下次直接从缓存中读取,即可加速访问速度。如下图所示的memory cache和disk cache:

Cache-Control - HTTP | MDN

Expires - HTTP | MDN

CDN加速

CDN(Content Distribute Network,内容分发网络):本质是一个缓存,部署在网络提供商的机房,使用户在请求网站服务时,可以从距离自己最近的网络提供商机房获取数据(指静态资源)。

反向代理缓存

在反向代理服务器缓存用户请求的静态资源:

启用压缩

对html、css、JavaScript启用Gzip压缩,压缩率很高,可以有效减少请求传输时间。

实现方式:

Nginx启用GZip压缩后端应用程序也可以启用压缩,比如pjl-comp-filter SpringBoot 2.0.0+通过参数配置即可:pression.enabled

改代码

减少http请求次数

每次HTTP需建立连接才能传输,减少请求次数,即可提高访问性能。

主要手段:

前端合并CSS、JavaScript、图片:就像webpack打包一样,最终只有几个JS、几个CSS、几张图片后端合并接口:有些接口设计不合理,相关数据可以考虑合并到一个接口中返回

CSS放在页面最上面,JS放在页面最下面

CSS放在最上面,可以快速进行页面渲染(用户更快看到页面显示出来);JS放在页面最下面,防止加载JS阻塞页面。

减少Cookie传输

Cookie会包含在request和reponse中,太大的Cookie会影响数据传输速度。

解决办法:

慎重考虑Cookie中保存的数据,比如有些数据是不是可以存储来localStorage中避免静态资源请求时发送Cookie

后端性能优化

分布式缓存

网站性能优化第一定律:使用缓存优化性能。

可以考虑各种分布式缓存,比如Redis、Memcached、JBoss Cache等。

异步操作(消息队列)

使用消息队列将调用异步化,不仅可以改善系统性能,还能改善网站的扩展性。

正常流程:发送请求 -> 保存数据到数据库 -> 响应返回

加消息队列的流程:发送请求 -> 进入消息队列 -> 响应返回,另外的进程消费消息队列中的消息

使用集群

使用负载均衡技术为一个应用构建由多态服务器组成的服务器集群,将并发访问请求分发到多台服务器上处理。

按照上图的负载均衡配置(假定为轮询方式),每台服务器只需要处理总并发请求的三分之一就可以了。

代码优化

合理优化业务代码,也可以很好的改善网站性能。

多线程

多用户并发访问是用户的基本需求,目前主要的Web应用服务器(比如Tomcat)都采用多线程的方式响应并发用户请求,因此网站开发天然就是多线程编程。

从资源利用的角度看,使用多线程的原因有两个:

IO阻塞:当前线程进行IO处理时,会被阻塞释放CPU以等待IO操作完成,由于IO操作(不管是磁盘IO还是网络IO)通常需要较长时间,这时CPU可以调度其它线程进行处理。这样即可最大程度利用CPU资源。多CPU:服务器多CPU的情况下,要想最大程度使用CPU,必须启用多线程

Web应用服务器和应用本身都可以创建多线程,那么创建多少线程合适呢?

启动线程数 = [任务执行时间 / (任务执行时间 - IO等待时间)] * CPU内核数

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

资源复用

系统运行时,要尽量减少那些开销很大的系统资源的创建和销毁,比如线程、数据库连接、网络通信连接、复杂对象等。从编程角度,资源复用主要有俩种模式:

单例模式:像Spring容器中的对象,默认都是单例对象池(Object Pool):复用对象实例,减少对象的创建和资源消耗。使用的时候从资源池中获取,使用完成后再放回到资源池中。比如线程池、数据库连接池等

数据结构

早期关于程序的定义是这样:程序 = 数据结构 + 算法。数据结构对程序的重要性不言而喻,在不同场景使用合适的数据结构,可以大大改善程序性能。

垃圾回收(JVM)

如果程序运行在JVM中,那么GC会对程序性能产生巨大影响。合理的对JVM参数进行调优,可以提高系统性能。

以JVM为例,其内存分为:

堆(heap):存储对象,对象的创建和释放、垃圾回收都在这里堆栈(stack):存储线程上下文信息,如方法参数,局部变量等

JVM分代垃圾回收原理:

堆分成年轻代(Young Generation)和年老代(Old Generation)Young Generation分成Eden Space、From区和To区 新建对象总是在Eden Space,当Eden Space空间满后,就触发一次Young GC,并将还被使用的对象复制到From区,这样整个Eden去都是未被使用的空间当Eden区再次用完,再次触发Young GC,将Eden Space和From区还在被使用的对象复制到To区,下一次Young GC则是将Eden区和To区还被使用的对象复制到From区【对吗?】 经过多次Young GC,某些对象会在From区和To区多次复制,如果超过某个阈值对象还没释放,则将该对象复制到Old Generation。如果Old Generation空间也以用完,那么就会触发FULL GC,即所谓的全量回收。全量回收会对系统性能产生较大影响。因此需要合理设置年轻代和年老代的大小,以减少FULL GC的次数。

SQL性能优化

有时整个应用响应时间几乎接近于SQL执行时间,所以针对某些执行时间长的SQL,可以考虑作SQL优化。

常规思路:使用EXPLAIN/DESCRIBE查看执行计划,根据执行计划添加索引索引失效的情况 LIKE关键字后面第一个字符就是%,所以一般都是start with方式匹配(以前老外就是这么做的)组合索引,没有按左子集匹配,按定义顺序来匹配 优化数据结构 数据冗余分离使用频度很少的字段 创建中间表,直接查中间表,但需要维护中间表 优化插入速度 禁用索引禁用唯一性校验 优化MySQL参数

参考《MySQL 5.5从零开始学》

存储性能优化

有时候,系统的瓶颈是磁盘读写能力。

使用SSD替代HDD

网站中大部分访问数据是随机的,这种情况下SSD具有更好的性能。

HDD:快速顺序读写、慢速随机读写、可能存在磁盘震动、不防摔、价格便宜SSD :快速顺序读写、快速随机读写、磕碰不容易坏、价格稍贵

B+数 vs LSM树

为了改善数据访问性能,文件系统或数据库系统通常会对数据排序后存储,以加快数据检索速度,这就需要保证数据在不断更新、插入、删除后依然有序,传统关系数据库的做法是使用B+数。

B+树是一种专门针对磁盘存储而优化的N叉排序树,以树节点为单位存储在磁盘中,从根节点查询所需数据所在的节点编号和磁盘位置,将其加载到内存中后继续查找,知道找到所需的数据。

目前数据库多采用两级索引的B+树,树的层次最多三层。因此可能需要5次磁盘访问才能更新一条记录(三次磁盘访问获得数据索引及行ID,然后再进行一次数据文件度操作以及一次数据文件写操作)。

但是由于每次磁盘访问是随机的,而传统机械硬盘在随机访问时性能较差,每次数据访问都需要多次访问磁盘从而影响数据访问性能。

目前许多NoSQL产品采用LSM树作为主要数据结构,LSM树可以看作是一个N阶合并树。数据写操作(插入、修改、删除)都在内存中进行,并且都会创建一个新记录(修改会记录新的数据值,而删除会记录一个删除标志),这些数据在内存中仍然还是一颗排序树,当数据超过设定的内存阈值后,会将这颗排序树和磁盘上最新的排序树合并。当这颗排序树的数据量也超过设定阈值后,和磁盘上下一级的排序树合并。合并过程中,会用最新更新的数据覆盖旧的数据(或者记录为不同版本)。

在需要进行读操作,总是先从内存中的排序树开始搜索,如果没有找到再去磁盘中的排序树上顺序查找。在LSM树上进行一次数据更新不需要磁盘访问,在内存即可完成,速度远快于B+树。当数据访问以写操作为主,而读操作集中在最近写入的数据上时,使用LSM树可以极大程度地减少磁盘访问次数,加快访问速度。

RAID vs HDFS

RAID(廉价磁盘冗余阵列)技术主要是为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力。常用RAID技术上有如下几种(假设服务器有N块磁盘,要存储的数据就是Data):

RAID0:数据分成N份,每个磁盘存一份,写入速度是原来的N倍,读取也是。但是没做备份,随便哪一个磁盘损坏,那么整体数据损坏。RADI1:数据总是写双份,可靠性极高 RAID10:集合RAID0和RAID1,在RAID0基础上总是写双份数据。即提高的速度,又改善了性能,但是磁盘利用率低,有一半都是备份。RAID3:一般情况下,一台服务器不会同时出现损坏两块磁盘的情况,在只损坏其中一块磁盘的情况下, 如果能利用其它磁盘的数据恢复损坏磁盘,这样在保证可靠性和性能的同时,磁盘利用率也得到大幅提升。在数据写入时,把数据分成N-1块,并发写入到N-1块磁盘,并在第N块盘上记录校验数据,任何一块磁盘损坏(包括数据检验盘),都可以利用其它N-1块磁盘的数据恢复。N盘一直使用,可能会经常损坏,实际情况中,很少使用RAID3。 RAID5:在RAID3的基础上,校验数据不写入到第N块磁盘,二是螺旋式写入所有磁盘中,这样就比较平均,避免了RAID3中频繁写入第N块盘,从而导致第N块盘损坏的问题。RAID6:在RAID5的基础上,数据只写入N-2块盘,并螺旋式地在两块磁盘中写入校验信息(使用不同算法生成)。这样可以在RAID5基础上提高可靠性,比如两台磁盘都坏了。

各个RAID的比较:

HDFS以块(Block)为单位管理文件内容,一个文件被分割成若干个Block,当应用程序写文件时,没写完一个Block,HDFS就将其自动复制到另外两台机器上,保证每个Block都有三个副本,即使有台服务器宕机,数据依然可以访问,相当于实现了RAID1的数据复制功能。

当对文件进行处理计算时,通过MapReduce并发计算任务框架,可以启动多个计算子任务(MapReduce Task),同时读取文件的多个Block,并发处理,相当于实现了RAID0的并发访问功能。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。