个人专题目录
深入了解性能优化
1.常用的性能评价/测试指标
-
响应时间
- 提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。
-
并发数
- 同一时刻,对服务器有实际交互的请求数。
和网站在线用户数的关联:1000个同时在线用户数,可以估计并发数在5%到15%之间,也就是同时并发数在50~150之间。
- 同一时刻,对服务器有实际交互的请求数。
-
吞吐量
- 对单位时间内完成的工作量(请求)的量度
-
相互之间的关系
- 系统吞吐量和系统并发数以及响应时间的关系:
理解为高速公路的通行状况:
吞吐量是每天通过收费站的车辆数目(可以换算成收费站收取的高速费),
并发数是高速公路上的正在行驶的车辆数目,
响应时间是车速。
车辆很少时,车速很快。但是收到的高速费也相应较少;随着高速公路上车辆数目的增多,车速略受影响,但是收到的高速费增加很快;
随着车辆的继续增加,车速变得越来越慢,高速公路越来越堵,收费不增反降;
如果车流量继续增加,超过某个极限后,任务偶然因素都会导致高速全部瘫痪,车走不动,当然后也收不着,而高速公路成了停车场(资源耗尽)。
2.常用的性能优化手段
-
总原则
-
避免过早优化
不应该把大量的时间耗费在小的性能改进上,过早考虑优化是所有噩梦的根源。
所以,我们应该编写清晰,直接,易读和易理解的代码,真正的优化应该留到以后,等到性能分析表明优化措施有巨大的收益时再进行。
但是过早优化,不表示我们应该编写已经知道的对性能不好的的代码结构。 -
进行系统性能测试
所有的性能调优,都有应该建立在性能测试的基础上,直觉很重要,但是要用数据说话,可以推测,但是要通过测试求证。
-
寻找系统瓶颈,分而治之,逐步优化
性能测试后,对整个请求经历的各个环节进行分析,排查出现性能瓶颈的地方,定位问题,分析影响性能的的主要因素是什么?内存、磁盘IO、网络、CPU,还是代码问题?架构设计不足?或者确实是系统资源不足?
-
-
前端优化手段
-
浏览器/App
- 减少请求数;
合并CSS,Js,图片
- 使用客户端缓冲;
静态资源文件缓存在浏览器中,有关的属性Cache-Control和Expires
如果文件发生了变化,需要更新,则通过改变文件名来解决。- 启用压缩
减少网络传输量,但会给浏览器和服务器带来性能的压力,需要权衡使用。
- 资源文件加载顺序
css放在页面最上面,js放在最下面
- 减少Cookie传输
cookie包含在每次的请求和响应中,因此哪些数据写入cookie需要慎重考虑
- 给用户一个提示
有时候在前端给用户一个提示,就能收到良好的效果。毕竟用户需要的是不要不理他。
- CDN加速
CDN,又称内容分发网络,本质仍然是一个缓存,而且是将数据缓存在用户最近的地方。无法自行实现CDN的时候,可以考虑商用CDN服务。
- 反向代理缓存
将静态资源文件缓存在反向代理服务器上,一般是Nginx。
- WEB组件分离
将js,css和图片文件放在不同的域名下。可以提高浏览器在下载web组件的并发数。因为浏览器在下载同一个域名的的数据存在并发数限制。
-
应用服务性能优化
-
存储性能优化
- 选择合适的数据结构
选择ArrayList和LinkedList对我们的程序性能影响很大,为什么?因为ArrayList内部是数组实现,存在着不停的扩容和数据复制。
- 选择更优的算法
举个例子,最大子列和问题:
给定一个整数序列,a0, a1, a2, …… , an(项可以为负数),求其中最大的子序列和。
如果所有整数都是负数,那么最大子序列和为0;
例如(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,
最大子段和为20,子段为a[2],a[3],a[4]。
最坏的算法:穷举法,所需要的的计算时间是O(n^3).
一般的算法:分治法的计算时间复杂度为O(nlogn).
最好的算法:最大子段和的动态规划算法,计算时间复杂度为O(n)
n越大,时间就相差越大,比如10000个元素,最坏的算法和最好的算法之间的差距绝非多线程或者集群化能轻松解决的。- 编写更少的代码
同样正确的程序,小程序比大程序要快,这点无关乎编程语言。
3.应用服务性能优化
-
缓存
- 缓存的基本原理和本质
- 缓存是将数据存在访问速度较高的介质中。可以减少数据访问的时间,同时避免重复计算。
- 合理使用缓冲的准则
- 频繁修改的数据,尽量不要缓存,读写比2:1以上才有缓存的价值。
- 缓存一定是热点数据。
应用需要容忍一定时间的数据不一致。 - 缓存可用性问题,一般通过热备或者集群来解决。
- 缓存预热,新启动的缓存系统没有任何数据,可以考虑将一些热点数据提前加载到缓存系统。
- 解决缓存击穿:
1、布隆过滤器,或者2、把不存在的数据也缓存起来 ,比如有请求总是访问key = 23的数据,但是这个key = 23的数据在系统中不存在,可以考虑在缓存中构建一个( key=23 value = null)的数据。
- 分布式缓存与一致性哈希
- 以集群的方式提供缓存服务,有两种实现;
- 需要更新同步的分布式缓存,所有的服务器保存相同的缓存数据,带来的问题就是,缓存的数据量受限制,其次,数据要在所有的机器上同步,代价很大。
- 每台机器只缓存一部分数据,然后通过一定的算法选择缓存服务器。常见的余数hash算法存在当有服务器上下线的时候,大量缓存数据重建的问题。所以提出了一致性哈希算法。
- 一致性哈希
- 首先求出服务器(节点)的哈希值,并将其配置到0~232的圆(continuum)上
- 然后采用同样的方法求出存储数据的键的哈希值,并映射到相同的圆上。
- 然后从数据映射到的位置开始顺时针查找,将数据保存到找到的第一个服务器上。如果超过232仍然找不到服务器,就会保存到第一台服务器上。
- 一致性哈希算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。
- 一致性哈希算法在服务节点太少时,容易因为节点分部不均匀而造成数据倾斜问题,此时必然造成大量数据集中到Node A上,而只有极少量会定位到Node B上。为了解决这种数据倾斜问题,一致性哈希算法引入了虚拟节点机制,即对每一个服务节点计算多个哈希,每个计算结果位置都放置一个此服务节点,称为虚拟节点。具体做法可以在服务器ip或主机名的后面增加编号来实现。例如,可以为每台服务器计算三个虚拟节点,于是可以分别计算 “Node A#1”、“Node A#2”、“Node A#3”、“Node B#1”、“Node B#2”、“Node B#3”的哈希值,于是形成六个虚拟节点:同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到Node A上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为32甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。
- 以集群的方式提供缓存服务,有两种实现;
- 缓存的基本原理和本质
集群
-
异步
- 同步和异步,阻塞和非阻塞
- 常见异步的手段
- Servlet异步
- servlet3中才有,支持的web容器在tomcat7和jetty8以后。
- 多线程
- 消息队列
- 集群
- 可以很好的将用户的请求分配到多个机器处理,对总体性能有很大的提升
- 程序代码级别
- Servlet异步
-
应用相关
- 代码级别
- 选择合适的数据结构
- 选择更优的算法
- 编写更少的代码
- 并发编程
- 充分利用CPU多核
- 实现线程安全的类,避免线程安全问题
- 同步下减少锁的竞争
- 资源的复用
- 目的是减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信连接,线程资源等等。
- JVM
- 对于程序来说,通常只有一部分代码被经常执行,这些关键代码被称为应用的热点,执行的越多就认为是越热。将这些代码编译为本地机器特定的二进制码,可以有效提高应用性能。
- 存储性能优化
- 尽量使用SSD
- 定时清理数据或者按数据的性质分开存放
- 代码级别