文章目录
- 系统架构之高性能
- 如何发现性能瓶颈?
- 不同视角下的系统性能
- 用户视角的系统性能
- 开发人员视角的系统性能
- 运维人员视角的系统性能
- 性能指标
- 性能测试方法
- 性能优化策略
- 性能分析
- 性能优化
- Web前端性能优化
- 浏览器访问优化
- 1.减少http请求
- 2.使用浏览器缓存
- 3.启用压缩
- 4.CSS 放在页面最上面、JavaScript 放在页面最下面
- 5.减少 Cookie 传输
- CDN 加速
- 应用服务器性能优化
- 缓存
- 如何合理使用缓存?
- 分布式缓存
- 异步操作
- 集群
- 代码优化
- 多线程
- 解决线程安全的策略
- 资源复用
- 数据结构
- 垃圾回收
- 存储性能优化
- 机械硬盘 vs. 固态硬盘
- RAID vs. HDFS
系统架构之高性能
如何发现性能瓶颈?
性能测试是性能优化的前提和基础,也是性能优化结果的检查和度量标准
不同视角下的系统性能
用户视角的系统性能
主要优化手段,优化页面 HTML 样式、利用浏览器端的并发和异步特性、调整浏览器缓存策略,使用 CDN 服务,反向代理手段
开发人员视角的系统性能
主要关注应用程序本身及其相关子系统的性能,包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标
主要优化手段有使用缓存加速数据读取,使用集群提高吞吐能力,使用异步消息加快请求响应以及实现削峰,使用代码优化手段改善程序性能
运维人员视角的系统性能
运维人员更加住基础设施性能和资源利用率
主要优化手段有建设优化骨干网,使用高性价比定制服务器,利用虚拟化技术优化资源利用
性能指标
响应时间、并发数、吞吐量(TPS(每秒事务数)、QPS(每秒查询数)、HPS(每秒 HTTP 请求数))、性能计数器 (是描述服务器或主机系统性能的一些数据指标,一般包括 System Load,对象与线程数,内存使用,CPU 使用,磁盘与网络 I/O 等指标)
性能测试方法
性能测试 -> 负载测试 -> 压力测试 -> 稳定性测试
性能优化策略
性能分析
排查一个系统的性能瓶颈和排查一个程序的性能瓶颈的手法基本相同:
- 检查请求处理的各个环节,分析哪个响应时间不合理、超过预期
- 检查监控数据,分析影响性能的主要因素是内存、磁盘、网络、还是 CPU
- 是代码问题还是架构设计不合理,或者系统资源确实不足
性能优化
定位到产生性能问题的具体原因后,就需要进行性能优化,根据系统的分层架构,可分为 Web 前端性能优化、应用服务器性能优化、存储服务器性能优化
Web前端性能优化
浏览器访问优化
1.减少http请求
- 主要手段是合并 CSS、合并 JavaScript、合并图片,将浏览器一次访问需要的 JS 或者 CSS 合并成一个文件
- 合并图标为雪碧图,减少请求次数
- 静态资源使用独立的域名,突破浏览器对同一个域名访问资源的限制
2.使用浏览器缓存
对于一个 Web 系统而言,CSS,JS,LOGO,图标这些静态资源文件更新频率都比较低,而这些文件几乎是每次 HTTP 请求都需要的,如果将这些文件缓存在浏览器中,可以极好的改善性能,通过设置 HTTP 都中的 Cache-Control 和 Expires 属性,可以设定浏览器缓存
3.启用压缩
在服务端进行压缩,在浏览器端对文件进行解压缩,可以有效减少通信传输的数据量。 文本文件的压缩效率可达到 80% 以上,因此HTML、CSS、Javascript 文件启用 Gzip 压缩可达到较好的效果。但是压缩对于服务器和浏览器产生一定的压力,在通信带宽良好,而服务器资源不足的情况下要权衡考虑
4.CSS 放在页面最上面、JavaScript 放在页面最下面
浏览器会在下载完全部 CSS 之后才对整个页面进行渲染,因此最好的做法是将 CSS 放在页面最上面,让浏览器尽快下载 CSS。JavaScript 则相反,浏览器在加载 JavaScript 后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此 JavaScript 最好放在页面最下面,但如果页面解析时就需要用到 JavaScript,这时放在底部就不合适了
5.减少 Cookie 传输
一方面,Cookie 包含在每次请求和响应中,太大的 Cookie 会严重影响数据传输,因此那些数据需要写入 Cookie 需要慎重考虑,尽量减少 Cookie 中的传输的数据量。另一方面,对于某些静态资源的访问,如 CSS、Script 等,发送 Cookie 没有意义,可以考虑使用独立域名进行访问,避免请求静态资源时发送 Cookie,减少 Cookie 传输次数
CDN 加速
CDN 的本质仍然是一个缓存,而且将数据缓存在离用户最近的地方,使用户以最快速度获取数据,即所谓网络访问的第一跳
CND 能够缓存的一般是静态资源,如图片、文件、CSS、Script 脚本、静态网页等,但是这些文件访问频度很高,将其缓存在 CDN 可极大改善网页的打开速度
应用服务器性能优化
优化的主要手段有缓存、集群、异步
缓存
当系统遇到性能瓶颈时,第一个想到的解决方案就是使用缓存。在整个网站应用中,缓存几乎无所不在,即存在于浏览器,也存在于应用服务器和数据库服务器;既可以对数据缓存,也可以对文件缓存,还可以对页面片段缓存,合理使用缓存,对系统系能优化意义重大
如何合理使用缓存?
使用缓存对提高系统性能有很多好处,但是不合理使用缓存,非但不能提高系统的性能,还会成为系统的累赘,甚至风险
**频繁修改的数据:**如果缓存中保存的是频繁修改的数据,就会出现数据写入缓存后,还没有来得及读,就已经失效的情形,徒增系统负担。一般来说,数据读写比在 2:1 以上,缓存才有意义
**没有热点的访问:**缓存使用内存作为存储,内存资源宝贵而有限,不可能将所有数据都缓存起来,只有最新访问的数据缓存起来,而将历史数据清理出缓存。如果应用系统访问数据没有热点,不遵循二八定律,即大部分数据访问并没有集中在小部分数据上,那么缓存就没有意义,因为大部分数据还没有被再次访问就已经被挤出缓存了
数据不一致与脏读:一般对缓存的数据设置失效时间,一旦超过失效时间,就要从数据库中重新加载。还有一种策略是数据更新时立即更新缓存,不过这也会带来更多系统开销和事务一致性的问题
缓存可用性:缓存是为了提高数据读取性能的,缓存数据丢失或者缓存不可用不会影响到应用程序的处理,它可以从数据库中直接获取,可以通过主备等手段提高缓存可用性;当某台缓存服务器宕机时,将缓存访问切换到备份服务器上。通过分布式缓存服务器集群,将缓存数据分不到集群多台服务器上可以在一定程度上改善缓存的可用性。当一台缓存服务器宕机的时候,只有部分缓存数据丢失,重新从数据加载这部分数据不会对数据库产生很大的影响
缓存预热:在重启缓存时,对系统的性能和数据库负载都不太好,最好在缓存系统启动时就把热点数据加载好,这个缓存预加载手段叫做缓存预热(warm up)
缓存穿透:如果因为不恰当的业务、或者恶持续高并发地请求某个不存在的数据,由于缓存没有保存该数据,所有的请求都会落到数据库上,会对数据库造成很大的压力,甚至崩溃,一个简单的对策是将不存在的数据也缓存起来
分布式缓存
Memcached、Redis
一致性 hash 成为数据存储伸缩性架构设计经典范式
正是集群内服务器互不通信使得集群可以做到几乎无限制的线性伸缩,这也正是目前流行的许多大数据技术的基本架构特点
异步操作
使用消息队列将调用异步化,可改善网站的扩展性,开可以改善网站系统的性能
消息队列具有很好的削峰作用,即通过异步处理,将短时间高并发产生的事务消息存储在消息队列中,从而削平高峰期的并发事务。在电子商务网站促销活动中,合理使用消息队列,可有效抵御促销活动刚开始大量涌入的订单对系统造成的冲击
集群
在系统高并发访问的场景下,使用负载均衡技术为一个应用构建一个由多态服务器组成的服务器集群,将并发访问请求分发到多态服务器上处理,避免单一服务器因负载压力过大而响应缓慢,使用户请求具有更好的响应延迟特性
代码优化
多线程
从资源利用的角度看,使用多线程的原因主要有两个:IO 阻塞和多 CPU。当前线程进行 IO 处理的时候,会被阻塞释放 CPU 以等待 IO 操作完成,由于 IO 操作(不管是磁盘 IO 还是网络 IO)通常都需要较长的时间,这是 CPU 可以调度其他的线程进行处理
启动线程数 = [任务执行时间 / (任务执行时间 - IO 等待时间)] x CPU 内核数
最佳启动线程数和 CPU 内核数量成正比,和 IO 阻塞时间成反比。如果任务都是 CPU 计算型任务,那么线程数最多不超过 CPU内核数,因为启动再多线程,CPU 也来不及调度;相反如果是任务需要等待磁盘操作,网络响应,那么多启动线程有助于提高任务并发度,提高系统吞吐能力,改善系统性能
解决线程安全的策略
将对象设计为无状态对象
使用局部对象
并发访问资源时使用锁
资源复用
主要有两种模式:
单例(Singleton)
对象池(Object Pool)
数据结构
Hash 散列算法:
Time33 算法:即对字符串逐字符迭代乘以 33,求得 hash 值,算法原型:hash(i) = hash(i - 1) * 33 + str[i]
一种可行的方案是对字符串取信息指纹,在对信息指纹球 hashcode,由于字符串微笑的变化就可以引起信息指纹的巨大不同,因此可以获得较好的随机散列(MD5)
垃圾回收
可以参考对 JVM 的总结章节
就是精良减少 Full GC,尽量做到整个运行期做到不进行 Full GC
存储性能优化
在系统中,海量的数据对鞋对磁盘访问造成巨大的压力,虽然可以通过 Cache 解决一部分数据读压力,但是很多时候,磁盘任然是系统最严重的瓶颈。而且磁盘中存储的数据是网站中最重要的资产,磁盘的可用性和容错性也只管重要
机械硬盘 vs. 固态硬盘
RAID vs. HDFS
- RAID(廉价磁盘冗余阵列)技术主要是为了改善磁盘的访问延迟,增强磁盘的可用性和容错能力
- RAID0:数据从内存缓冲区写入磁盘,根据磁盘数量将数据分成 N 份,这些书同时并发写入 N 块磁盘,是的数据整体写入速度是一块磁盘的 N 倍。读取时也是一样,因此 RAID0 具有几块的数据读写速度,但是 RAID0 不做数据备份,N 块磁盘中只要有一块损坏,数据完整性就会破坏
- RAID1:数据在写入磁盘时,将一份数据同时写入两块磁盘,这样任何一块磁盘孙华都不会导致数据丢失,插入一块新磁盘就可以通过复制数据的方式自动修复,具有极高的可靠性
- RAID10:结合 RAID0 和 RAID1 两种方案,将所有磁盘平均分成两份,数据同时在两份磁盘写入,相当于 RAID1,但是在每份磁盘里面的N/2块磁盘上,采用 RAID0 技术技术并发读写,既提高可靠性又改善性能,不过 RAID10 的磁盘利用率较低,有一般的磁盘用来写备份数据
- RAID3:一般情况下,一台服务器上不会出现同时损坏两块磁盘的情况,在只损坏一块磁盘的情况下,如果能够利用其它的磁盘数据恢复损坏磁盘的数据,这样在保证可靠性和性能的同时,磁盘的利用率也得到大幅提升。在数据写入磁盘的时候,将数据分成 N-1份,并发写入 N-1块磁盘,并在第 N 块磁盘记录校验数据,任何一块磁盘损坏(包括校验数据磁盘)都可以利用其它的 N-1 块磁盘的数据修复,在再数据修改较多的场景中个,修改任何磁盘数据都会导致第N块磁盘重写校验数据,频繁写入的后果是第N磁盘比其它磁盘容易损坏,需要频繁更换,所以 RAID3 很少在实践中使用
- RAID5:比较常用,和 RAID3 类似,但是校验数据不是写入第 N 块磁盘,而是螺旋地写入所有磁盘中这样校验数据的修改也被平均到所有磁盘上,避免 RAID3 频繁写坏一块磁盘的情况
- RAID6: 能够保证在损坏两块盘的情况下仍然可以修复数据,与RAID5类似,但是数据只写入N-2块磁盘,并螺旋式地在两块磁盘中写入校验信息
使用 RAID 卡或者主板直接支持,也可以通过软件实现
HDFS(Hadoop分布式文件系统):
系统在在整个存储集群的多台服务器上进行数据并发读写和备份,可以看作在服务器集群规模上实现类似 RAID 的功能,因此不需要磁盘 RAID
HDFS 以块(Block(64M))为单位管理文件内容,一个文件被分割成若干个 Block,当应用程序写文件时,每写完一个 Block,HDFS 就将其自动复制到另外两台机器上,保证每个 Block 有三个副本,即使有两台服务器宕机,数据依然可以访问,相当于实现了 RAID1 的数据复制功能
当对文件进行处理计算时,通过 MapReduce 并发计算任务框架,可以启动多个子任务,同时读取文件的多个 Block,并发处理,相当于实现了 RAID0 的并发访问功能
NameNode -> DataNode
总结:
系统性能对最终用户而言是一种主观感受,性能优化的最终摸底就是改善用户的体验,是他们感觉系统很快。离开这个目的,追求技术上的所谓高性能,是舍本逐末,没有多大意义,而用户体验的快或者慢,可以通过技术手段改善,也可以通过优化交互体验改善
归根结底,技术是为业务服务的,技术选型和技术决策依赖业务规划乃至企业战略规划,离开业务发展的驱动,技术走不远,甚至还会迷路