性能测试是性能优化的前提和基础,也是性能优化结果的检查和度量标准
主要优化手段,优化页面 HTML 样式、利用浏览器端的并发和异步特性、调整浏览器缓存策略,使用 CDN 服务,反向代理手段
主要关注应用程序本身及其相关子系统的性能,包括响应延迟、系统吞吐量、并发处理能力、系统稳定性等技术指标
主要优化手段有使用缓存加速数据读取,使用集群提高吞吐能力,使用异步消息加快请求响应以及实现削峰,使用代码优化手段改善程序性能
运维人员更加住基础设施性能和资源利用率
主要优化手段有建设优化骨干网,使用高性价比定制服务器,利用虚拟化技术优化资源利用
响应时间、并发数、吞吐量(TPS(每秒事务数)、QPS(每秒查询数)、HPS(每秒 HTTP 请求数))、性能计数器 (是描述服务器或主机系统性能的一些数据指标,一般包括 System Load,对象与线程数,内存使用,CPU 使用,磁盘与网络 I/O 等指标)
性能测试 -> 负载测试 -> 压力测试 -> 稳定性测试
排查一个系统的性能瓶颈和排查一个程序的性能瓶颈的手法基本相同:
定位到产生性能问题的具体原因后,就需要进行性能优化,根据系统的分层架构,可分为 Web 前端性能优化、应用服务器性能优化、存储服务器性能优化
对于一个 Web 系统而言,CSS,JS,LOGO,图标这些静态资源文件更新频率都比较低,而这些文件几乎是每次 HTTP 请求都需要的,如果将这些文件缓存在浏览器中,可以极好的改善性能,通过设置 HTTP 都中的 Cache-Control 和 Expires 属性,可以设定浏览器缓存
在服务端进行压缩,在浏览器端对文件进行解压缩,可以有效减少通信传输的数据量。 文本文件的压缩效率可达到 80% 以上,因此HTML、CSS、Javascript 文件启用 Gzip 压缩可达到较好的效果。但是压缩对于服务器和浏览器产生一定的压力,在通信带宽良好,而服务器资源不足的情况下要权衡考虑
浏览器会在下载完全部 CSS 之后才对整个页面进行渲染,因此最好的做法是将 CSS 放在页面最上面,让浏览器尽快下载 CSS。JavaScript 则相反,浏览器在加载 JavaScript 后立即执行,有可能会阻塞整个页面,造成页面显示缓慢,因此 JavaScript 最好放在页面最下面,但如果页面解析时就需要用到 JavaScript,这时放在底部就不合适了
一方面,Cookie 包含在每次请求和响应中,太大的 Cookie 会严重影响数据传输,因此那些数据需要写入 Cookie 需要慎重考虑,尽量减少 Cookie 中的传输的数据量。另一方面,对于某些静态资源的访问,如 CSS、Script 等,发送 Cookie 没有意义,可以考虑使用独立域名进行访问,避免请求静态资源时发送 Cookie,减少 Cookie 传输次数
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 解决一部分数据读压力,但是很多时候,磁盘任然是系统最严重的瓶颈。而且磁盘中存储的数据是网站中最重要的资产,磁盘的可用性和容错性也只管重要
使用 RAID 卡或者主板直接支持,也可以通过软件实现
HDFS(Hadoop分布式文件系统):
系统在在整个存储集群的多台服务器上进行数据并发读写和备份,可以看作在服务器集群规模上实现类似 RAID 的功能,因此不需要磁盘 RAID
HDFS 以块(Block(64M))为单位管理文件内容,一个文件被分割成若干个 Block,当应用程序写文件时,每写完一个 Block,HDFS 就将其自动复制到另外两台机器上,保证每个 Block 有三个副本,即使有两台服务器宕机,数据依然可以访问,相当于实现了 RAID1 的数据复制功能
当对文件进行处理计算时,通过 MapReduce 并发计算任务框架,可以启动多个子任务,同时读取文件的多个 Block,并发处理,相当于实现了 RAID0 的并发访问功能
NameNode -> DataNode
总结:
系统性能对最终用户而言是一种主观感受,性能优化的最终摸底就是改善用户的体验,是他们感觉系统很快。离开这个目的,追求技术上的所谓高性能,是舍本逐末,没有多大意义,而用户体验的快或者慢,可以通过技术手段改善,也可以通过优化交互体验改善
归根结底,技术是为业务服务的,技术选型和技术决策依赖业务规划乃至企业战略规划,离开业务发展的驱动,技术走不远,甚至还会迷路
ref: [大型网站技术架构]