Java性能优化学习1:理论基础学习与分析

2024-06-07 09:05:56 浏览数 (1)

性能是什么

性能:使用有限的资源在有限的时间内完成工作。 最主要的衡量因素就是时间,所以很多衡量指标,都可以把时间作为横轴。

加载缓慢的网站,会受到搜索排名算法的惩罚,从而导致网站排名下降。 因此加载的快慢是性能优化是否合理的一个非常直观的判断因素,但性能指标不仅仅包括单次请求的速度,它还包含更多因素。

性能衡量指标

1、性能指标:吞吐量QPS/TPS/HPS、响应速度 2、响应时间:平均响应时间AVG、百分位数 3、并发量 4、秒开率 5、正确性

吞吐量与响应速度

现在市面上大多数都是分布式的高并发应用,而现在最常用的衡量指标就是吞吐量和响应速度。

  • 如何理解吞吐量和响应速度呢。
  • 可以以路口的红绿灯为例子来模拟,当交通繁忙的时候,如果红绿灯放行时间很长,那么一定会有某一条路口排很长的队,这个时候对于某一辆在排队的车来说,从他等待排队到经过这个红绿灯路口的时间,就是响应时间。
  • 那么吞吐量可以理解为和响应速度有个相对的概念,如果红绿灯时间我们让他变短,那么这个时候每个路口对于某些车辆来说,响应时间可能会变短,但是这个时候频繁的切换红绿灯,就会导致单位时间通过的车辆减少,所以可以认为吞吐量减少了。

这里给定几个确定的开发常用词:

QPS 代表每秒查询的数量, TPS 代表每秒事务的数量, HPS 代表每秒的 HTTP 请求数量等, 这都是常用的与吞吐量相关的量化指标。

需要注意的是:在性能优化的时候,我们要搞清楚优化的目标,到底是吞吐量还是响应速度。

有些时候,虽然响应速度比较慢,但整个吞吐量却非常高,比如一些数据库的批量操作、一些缓冲区的合并等。虽然信息的延迟增加了,但如果我们的目标就是吞吐量,那么这显然也可以算是比较大的性能提升。

  • 一般认为:
  • 响应速度是串行执行的优化,通过优化执行步骤解决问题;
  • 吞吐量是并行执行的优化,通过合理利用计算资源达到目标。

一般对于普通的业务来说,优化主要侧重于响应速度,如果响应速度提升了,吞吐量也就提升了。

而现在的环境是追求高并发、高可用、能够抵御住双十一特别大人流的场景、甚至可能还会夹带有云原生的概念,而这些业务场景,两者是都需要的。

响应时间衡量

如何计算响应时间呢?

  • 平均响应时间
  • 也就是常说的指标AVG,能够体现某个业务的平均处理能力。全部加起来除以总数即可。
  • 但是这么算也有弊端,那就是如果有长尾请求的话,长尾请求的影响会被很快平均,导致很多用户的请求变慢,这不能体现在平均耗时指标中。(什么是长尾请求:明显高于均值的那部分占比比较小的请求,业界关于延迟有一个P99的标准,也就是说99%的请求延迟要满足在一定耗时内,剩下的1%会大于这个耗时,而这个1%就会认为是长尾请求。)
  • 既然这里提到了长尾请求,那就顺带讲解一下长尾导致的危害、造成的原因、解决方案。

长尾危害:假设,一个接口提供服务B,有1%的可能性响应时间大于1s,如果此刻一个上游服务A需要完成一次查询,需要同时查询100次的话,那么服务A响应时间超过1s的概率是63%。

0.99的概率是小于1s,100次的概率是0.99^100 = 0.37,小于1s响应的时间是37%的概率。

那么请求大于1s的概率就是63%。

即使服务处理时间超过1秒的比例仅为 0.01% ,当需要同时查询的实例数(Numbers of Servers)达到2000时,服务延时大于1秒的请求数将超过18%。

  • 造成长尾的原因:
  • 共享资源竞争, 周期性的垃圾回收, 运维活动(比如日志备份), 硬件或者软件故障,网络的抖动,都有可能造成。
  • 解决长尾的方法:
  • 微博motan有一种双发机制,它可以有效解决长尾问题,同时能提升系统吞吐量。

传统解决接口超时问题可能通过重试,在一次请求发送之后等待指定的超时时间,如果没有返回则再请求一次,最差情况下要消耗 2 倍的超时时间。

而双发机制则不然,在发送一次请求后等待 P90(在 T1 时间内有 90% 的请求都能返回则称 P90=T1,通常系统的 P90 和程序设置的超时时间相比小很多)时间。

如果请求没有返回则在此刻再次发送一次请求,在超时时间内,这两个请求中取最快返回的那个。

当然,这里有个防雪崩机制,假如,超过一定数量的请求(比如 15%)都在进行双发,则认为服务整体有问题,会自动停止双发。实践证明,双发机制的去长尾效果非常明显

话说回来,为了解决平均响应时间的缺点,同时还要衡量响应时间,我们引入了百分位数概念。

  • 百分位数 这个也比较好理解。圈定一个时间范围,把每次请求的耗时加入一个列表中,然后按照从小到大的顺序将这些时间进行排序。这样,我们取出特定百分位的耗时,这个数字就是 TP 值。可以看到,TP 值(Top Percentile)和中位数、平均数等是类似的,都是一个统计学里的术语。

它的意义是,超过 N% 的请求都在 X 时间内返回。比如 TP90 = 50ms,意思是超过 90th 的请求,都在 50ms 内返回。

这个指标也是非常重要的,它能够反映出应用接口的整体响应情况。比如,某段时间若发生了长时间的 GC,那它的某个时间段之上的指标就会产生严重的抖动,但一些低百分位的数值却很少有变化。

我们一般分为 TP50、TP90、TP95、TP99、TP99.9 等多个段,对高百分位的值要求越高,对系统响应能力的稳定性要求越高。

在这些高稳定性系统中,目标就是要干掉严重影响系统的长尾请求。这部分接口性能数据的收集,会采用更加详细的日志记录方式,而不仅仅靠指标。比如,将某个接口,耗时超过 1s 的入参及执行步骤,详细地输出在日志系统中。

并发量

并发量是指系统同时能处理的请求数量,这个指标反映了系统的负载能力。

在高并发应用中,仅仅高吞吐是不够的,它还必须同时能为多个用户提供服务。并发高时,会导致很严重的共享资源争用问题,我们需要减少资源冲突,以及长时间占用资源的行为。

针对响应时间进行设计,一般来说是万能的。因为响应时间减少,同一时间能够处理的请求必然会增加。值得注意的是,即使是一个秒杀系统,经过层层过滤处理,最终到达某个节点的并发数,大概也就五六十左右。我们在平常的设计中,除非并发量特别低,否则都不需要太过度关注这个指标。

秒开率

现在的用户,如果上网,试想一下,打开一个页面如果要5-6秒那这个网页或者说APP估计评分肯定非常低,秒开是一种特别需要重视的用户体验。

正确性

在进行测试的时候,发现接口响应非常流畅,把并发数增加到 20 以后,应用接口响应依旧非常迅速。

但等应用真正上线时,却发生了重大事故,这是因为接口返回的都是无法使用的数据。

其问题原因也比较好定位,就是项目中使用了熔断。在压测的时候,接口直接超出服务能力,触发熔断了,但是压测并没有对接口响应的正确性做判断,造成了非常低级的错误。

优化的理论方法

理论方法有很多,如木桶理论、基础测试、Amdahal定律等等。

1、木桶理论

一只木桶若想要装最多的水,则需要每块木板都一样长而且没有破损才行。如果有一块木板不满足条件,那么这只桶就无法装最多的水。

能够装多少水,取决于最短的那块木板,而不是最长的那一块。

木桶效应在解释系统性能上,也非常适合。组成系统的组件,在速度上是良莠不齐的。系统的整体性能,就取决于系统中最慢的组件。

比如,在数据库应用中,制约性能最严重的是硬盘的 I/O 问题,也就是说,硬盘是这个场景下的短板,我们首要的任务就是补齐这个短板。

2、基准测试、预热

基准测试(Benchmark)并不是简单的性能测试,是用来测试某个程序的最佳性能。

应用接口往往在刚启动后都有短暂的超时。在测试之前,需要对应用进行预热,消除 JIT 编译器等因素的影响。而在 Java 里就有一个组件,即 JMH,就可以消除这些差异。

优化注意事项

1、把性能分析放在第一位而不是性能优化

在优化的时候不能凭借对代码的熟悉来猜测系统的问题所在,一般来说,复杂的业务系统往往有多个影响因素,我们应该将性能分析放在第一位,而不是把性能优化放在第一位。

进行性能优化时,我们一般会把分析后的结果排一个优先级(根据难度和影响程度),从大处着手,首先击破影响最大的点,然后将其他影响因素逐一击破。

有些优化会引入新的性能问题,有时候这些新问题会引起更严重的性能下降,你需要评估这个连锁反应,确保这种优化确实需要,同时需要使用数字去衡量这个过程,而不是靠感觉猜想。

2、进行大量试验,不依靠小数据量

个体请求的小批量数据,可参考价值并不是非常大。响应时间可能因用户的数据而异,也可能取决于设备和网络条件。

合理的做法,是从统计数据中找到一些规律,比如上面所提到的平均响应时间、TP 值等,甚至是响应时间分布的直方图,这些都能够帮我们评估性能质量。

3、不要过早优化和过度的优化

虽然性能优化有这么多好处,但并不代表我们要把每个地方都做到极致,性能优化也是要有限度的。程序要运行地正确,要比程序运行得更快还要困难。

计算机科学的鼻祖"Donald Knuth" 曾说:“过早的优化是万恶之源”,就是这个道理。

正确的做法是,项目开发和性能优化,应该作为两个独立的步骤进行,要做性能优化,要等到整个项目的架构和功能大体进入稳定状态时再进行。

4、保持良好的编程习惯

0 人点赞