全方面带你透彻探索服务优化技术方案
- 前提背景
- 影响一个系统性能的方方面面
- 代码优化
- 数据库优化
- 网络优化
- 硬件优化
- 常用的性能评价/测试指标
- 响应时间
- 并发数
- 吞吐量
- 响应时间、并发数和吞吐量之间的关系
- 运作流程关系
- 性能优化方案的建议
- 避免过早优化
- 进行系统性能测试
- 寻找系统瓶颈,分而治之,逐步优化
- 前端优化常用手段
- 面向浏览器/App的优化方向
- 减少请求数
- 使用客户端缓冲
- 启用压缩
- 减少Cookie传输
- 给用户一个提示
- 使用CDN加速
- 开启反向代理缓存
- WEB组件分离
- 应用服务性能优化
- 层级化缓存的用法和处理
- 缓存的基本原理和本质
- 合理使用缓冲的准则
- 同步和异步,阻塞和非阻塞
- 同步阻塞
- 同步非阻塞
- 异步阻塞
- 异步非阻塞
- 常见异步的手段
- 服务或系统的分布式/集群
- 分布式缓存与一致性哈希
- 一致性哈希:
- 并发编程技术
- 资源的复用
- 存储性能优化
- 总结
前提背景
当今的软件系统越来越复杂,需要不断地进行优化以提高其性能和稳定性。本文将介绍一些常见的软件系统优化技术,帮助开发人员更好地优化软件系统。
影响一个系统性能的方方面面
一个 web 应用不是一个孤立的个体,它是一个系统的部分,系统中的每一部分都会影响整个系统的性能。
代码优化
代码优化是软件系统优化的基础。通过对代码进行优化,可以提高软件系统的性能和稳定性。代码优化的方法包括:
-
减少代码的复杂度,尽量避免使用过多的嵌套和循环。
-
减少代码的冗余,尽量避免重复的代码。
-
使用高效的算法和数据结构,避免使用低效的算法和数据结构。
-
尽量减少内存和CPU的使用,避免内存泄漏和CPU占用过高。
数据库优化
数据库是软件系统中重要的组成部分,对其进行优化可以提高软件系统的性能和稳定性。数据库优化的方法包括:
-
设计合理的数据库结构,避免冗余和重复的数据。
-
使用索引来加速查询操作。
-
避免使用过多的连接和事务,尽量减少数据库的负载。
-
定期清理无用的数据和索引,避免数据库过大导致性能下降。
网络优化
网络是软件系统中重要的通信方式,对其进行优化可以提高软件系统的响应速度和稳定性。网络优化的方法包括:
-
使用高效的网络协议和传输方式,如HTTP/2和TCP/IP。
-
使用缓存来减少网络请求的次数。
-
避免使用过多的网络请求,尽量减少网络的负载。
-
使用CDN来加速网络请求的响应速度。
硬件优化
硬件是软件系统运行的基础,对其进行优化可以提高软件系统的性能和稳定性。硬件优化的方法包括:
-
使用高性能的CPU和内存。
-
使用高速的硬盘和网络设备。
-
避免过度使用硬件资源,尽量减少硬件的负载。
-
定期清理硬件设备,避免硬件故障导致软件系统崩溃。
总之,软件系统优化是一个复杂的过程,需要综合考虑代码、数据库、网络和硬件等多个方面。通过合理的优化方法,可以提高软件系统的性能和稳定性,为用户提供更好的使用体验。
常用的性能评价/测试指标
响应时间
提交请求和返回该请求的响应之间使用的时间,一般比较关注平均响应时间。常用操作的响应时间列表:
操作 | 响应时间 |
---|---|
访问页面 | 几秒 |
数据库查询一条记录(有索引) | 十几毫秒 |
机械磁盘一次寻址定位 | 4 毫秒 |
从机械磁盘顺序读取 1M 数据 | 2 毫秒 |
从 SSD 磁盘顺序读取 1M 数据 | 0.3 毫秒 |
从远程分布式换成 Redis 读取一个数据 | 0.5 毫秒 |
从内存读取 1M 数据 | 十几微妙 |
Java 程序本地方法调用 | 几微妙 |
网络传输 2Kb 数据 | 1 微妙 |
并发数
同一时刻,对服务器有实际交互的请求数。和网站在线用户数的关联:1000 个同时在线用户数,可以估计并发数在 5%到 15%之间,也就是同时并发数在 50~150 之间。
吞吐量
吞吐量 (throughput)表示在单位时间内通过某个网络 (或信道、接口)的实际的数据量 。 吞吐量更经常地用于对现实世界中的网络的一种测量,以便知道实际上到底有多少数据量能够通过网络。 显然,吞吐量受网络的带宽或网络的额定速率的限制。
响应时间、并发数和吞吐量之间的关系
理解为高速公路的通行状况。
- 吞吐量:每天通过收费站的车辆数目(可以换算成收费站收取的高速费)。
- 并发数:高速公路上的正在行驶的车辆数目,
- 响应时间:车运行的速度。
运作流程关系
-
车辆很少时,车速很快。但是收到的高速费也相应较少;
-
随着高速公路上车辆数目的增多,车速略受影响,但是收到的高速费增加很快;
-
随着车辆的继续增加,车速变得越来越慢,高速公路越来越堵,收费不增反降;
4, 如果车流量继续增加,超过某个极限后,任务偶然因素都会导致高速全部瘫痪,车走不动,当然后也收不着,而高速公路成了停车场(资源耗尽)。
性能优化方案的建议
避免过早优化
不应该把大量的时间耗费在小的性能改进上,过早考虑优化是所有噩梦的根源。所以,我们应该编写清晰,直接,易读和易理解的代码,真正的优化应该留到以后,等到性能分析表明优化措施有巨大的收益时再进行。但是过早优化,不表示我们应该编写已经知道的对性能不好的的代码结构。
进行系统性能测试
性能调优,都有应该建立在性能测试的基础上,直觉很重要,但是要用数据说话,可以推测,但是要通过测试求证。
寻找系统瓶颈,分而治之,逐步优化
性能测试后,对整个请求经历的各个环节进行分析,排查出现性能瓶颈的地方,定位问题,分析影响性能的的主要因素是什么?内存、磁盘 IO、网络、CPU,还是代码问题?架构设计不足?或者确实是系统资源不足?
前端优化常用手段
面向浏览器/App的优化方向
减少请求数
减少对于静态资源的渲染工作,尽量在合理的范围内进行合并CSS,JS,图片等静态资源文件。
使用客户端缓冲
静态资源文件缓存在浏览器中,有关的属性Cache-Control和Expires,如果文件发生了变化,需要更新,则通过改变文件名来解决。
启用压缩
减少网络传输量,但会给浏览器和服务器带来性能的压力,需要权衡使用。资源文件加载顺序css放在页面最上面,js放在最下面。
减少Cookie传输
cookie 包含在每次的请求和响应中,因此哪些数据写入 cookie 需要慎重考虑。
给用户一个提示
有时候在前端给用户一个提示,就能收到良好的效果。毕竟用户需要的是不要不理他。
使用CDN加速
CDN,又称内容分发网络,本质仍然是一个缓存,而且是将数据缓存在用户最近的地方。无法自行实现 CDN 的时候,可以考虑商用 CDN 服务
开启反向代理缓存
将静态资源文件缓存在反向代理服务器上,一般是Nginx。
WEB组件分离
将 js,css 和图片文件放在不同的域名下。可以提高浏览器在下载web组件的并发数。因为浏览器在下载同一个域名的的数据存在并发数限制。
应用服务性能优化
层级化缓存的用法和处理
网站性能优化第一定律:优先考虑使用缓存优化性能,缓存离用户越近越好。
缓存的基本原理和本质
缓存是将数据存在访问速度较高的介质中。可以减少数据访问的时间,同时避免重复计算。
合理使用缓冲的准则
-
频繁修改的数据,尽量不要缓存,读写比 2:1 以上才有缓存的价值,缓存一定是热点数据。
-
应用需要容忍一定时间的数据不一致。
-
缓存可用性问题,一般通过热备或者集群来解决。
-
缓存预热,新启动的缓存系统没有任何数据,可以考虑将一些热点数据提前加载到缓存系统。
同步和异步,阻塞和非阻塞
同步和异步关注的是结果消息的通信机制
-
同步:同步的意思就是调用方需要主动等待结果的返回。
-
异步:异步的意思就是不需要主动等待结果的返回,而是通过其他手段比如,状态通知,回调函数等。
阻塞和非阻塞关注的是等待结果返回调用方的状态
- 阻塞:结果返回之前,当前线程被挂起,不做任何事。
- 非阻塞:结果在返回之前,线程可以做一些其他事,不会被挂起。
同步阻塞
同步阻塞基本也是编程中最常见的模型,打个比方你去商店买衣服,你去了之后发现衣服卖完了,那你就在店里面一直等,期间不做任何事(包括看手机),等着商家进货,直到有货为止,这个效率很低。jdk 里的 BIO 就属于同步阻塞。
同步非阻塞
同步非阻塞在编程中可以抽象为一个轮询模式,你去了商店之后,发现衣服卖完了,这个时候不需要傻傻的等着,你可以去其他地方比如奶茶店,买杯水,但是你还是需要时不时的去商店问老板新衣服到了吗。jdk里的NIO就属于同步非阻塞。
异步阻塞
异步阻塞这个编程里面用的较少,有点类似你写了个线程池,submit,然后马上future.get(),这样线程其实还是挂起的。有点像你去商店买衣服,这个时候发现衣服没有了,这个时候你就给老板留给电话,说衣服到了就给我打电话,然后你就守着这个电话,一直等着他响什么事也不做。
异步非阻塞
好比你去商店买衣服,衣服没了,你只需要给老板说这是我的电话,衣服到了就打。然后你就随心所欲的去玩,也不用操心衣服什么时候到,衣服一到,电话一响就可以去买衣服了。jdk 里的 AIO 就属于异步非阻塞。
常见异步的手段
- Servlet异步技术(servlet3)
- 多线程技术手段
- 消息队列
服务或系统的分布式/集群
可以很好的将用户的请求分配到多个机器处理,对总体性能有很大的提升。例如缓存系统或者Redis中间件的分布式集群模式。
分布式缓存与一致性哈希
以集群的方式提供缓存服务,有两种实现;
-
需要更新同步的分布式缓存,所有的服务器保存相同的缓存数据,带来的问题就是,
缓存的数据量受限制,其次,数据要在所有的机器上同步,代价很大。 -
每台机器只缓存一部分数据,然后通过一定的算法选择缓存服务器。常见的余数 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”、“NodeB#3”的哈希值,于是形成六个虚拟节点:同时数据定位算法不变,只是多了一步虚拟节点到实际节点的映射,例如定位到“Node A#1”、“Node A#2”、“Node A#3”三个虚拟节点的数据均定位到 Node A 上。这样就解决了服务节点少时数据倾斜的问题。在实际应用中,通常将虚拟节点数设置为 32 甚至更大,因此即使很少的服务节点也能做到相对均匀的数据分布。
并发编程技术
- 充分利用 CPU 多核,
- 实现线程安全的类,避免线程安全问题
- 同步下减少锁的竞争
资源的复用
目的是减少开销很大的系统资源的创建和销毁,比如数据库连接,网络通信连接,线程资源等等。
- 单例模式
- 池化技术
存储性能优化
- 尽量使用 SSD
- 定时清理数据或者按数据的性质分开存放
总结
性能调优是个很复杂、很细致的过程,要根据实际情况调整,不同的机器、不同的应用、不同的性能要求调优的手段都是不同的。也没有一个放之四海而皆准的配置或者公式。再比如操作系统工具,和操作系统本身相关的所谓大页机制,都需要大家平时去积累,去观察,去实践。