大厂都是如何对高并发系统做性能优化的?

2021-12-07 12:30:07 浏览数 (1)

1 导读

高并发系统的奥义:高性能、高可用、可扩展。

  • 性能反应了系统的使用体验 都是上万QPS的系统,一个响应时间毫秒级,一个秒级,用户体验明显不同
  • 可用性则表示系统可以正常服务用户的时间 上万QPS的系统,一个可全年不停机且无异常,一个隔三差五就宕机
  • 可扩展性 流量可分为平时流量、峰值流量。峰值流量可能会是平时流量的几倍至几十倍,在应对峰值流量时,通常需在架构方案上做更多准备。易于扩展的系统能在短期内迅速扩容,更加平稳分摊峰值流量。

业务价值->承载高并发->性能优化。 一切的前提是业务价值需要。如果没有足够价值,那可读性才是第一,性能在需要的地方是no.1,但不需要的地方可能就是倒数第一。当下技术框架出来的软件差不到哪去,没有这种及时响应诉求的地方,削峰下慢慢跑就是了。(但工作中常需要在缺少价值的地方着手性能优化。异步,并发编程,逻辑缓存,算法真的会加剧系统的复杂度,得不偿失。如果没那个价值,简单才是王道)。

提高并发度

  • 要么加硬件
  • 要么降低服务响应时间

做为开发,我们的目光更聚焦在降低响应时间: 1.采用非阻塞的rpc调用(高效的远端请求模式,采用容器的覆盖网络我认为也算) 2.将计算密集和io密集的的逻辑分割开,单独线程池,调整线程比例压榨单机性能(或者说找拐点)。 3.做缓存,io耗时的缓存和计算耗时的缓存(多级缓存,数据压缩降低带宽)。 4.采用享元模式,用好对象池和本地线程空间,尽量减少对象创建与销毁的开销,提高复用。 5.业务拆分,像状态变化后的外部系统通知,业务监控,es或solr等副本数据同步等操作,无需在主流程中做的事都拆掉。走canal监听表数据变化,推mq保最终一致的方式从业务项目完全解偶出来。 6.fork_join,分而治之的处理大任务。并发编程,采用多线程并行的方式处理业务。(规避伪共享,减小锁力度,采用合适的锁)。 7.数据库配置优化,查询优化。(存储优化比较头疼,毕竟不按业务拆单点跑不掉,单点性能就要命。基本只能内存库先行,后台同步数据做持久。然后内存库多副本,自修复,保留一系列自修复失败的修复手段)

2 性能优化原则

业务导向

脱离业务问题,妄自过早优化会徒增系统复杂度,浪费开发时间,也因某些优化可能会对业务上有些折中考虑,还会影响业务。

剑指主要矛盾

优先优化主要的性能瓶颈点

量化指标

在优化过程中,要时刻了解优化让响应时间降低多少,提升多少吞吐量。

持续优化

高并发系统的业务逻辑都很复杂,出现性能问题也有多方面原因。因此,我们在做性能优化的时候要明确目标,比方说,支撑每秒1万次请求的吞吐量下响应时间在10ms,那么我们就需要持续不断地寻找性能瓶颈,制定优化方案,直到达到目标为止。

在以上四个原则的指引下,掌握常见性能问题的排查方式和优化手段,就一定能让你在设计高并发系统时更加游刃有余。

3 性能的度量指标

一般度量性能的指标是系统接口的响应时间,需要收集一段时间的响应时间数据,然后依据统计方法计算特征值,这些特征值就能够代表这段时间的性能情况。常见的特征值有以下几类。

平均值

这段时间所有请求的响应时间数据和/总请求数。 在一定程度上反应这段时间的性能,但它敏感较差,若这段时间有少量慢请求,在平均值上并不能反应出来。

最大值

段时间内所有请求响应时间最长的值。 问题在于过于敏感。

分位值

有很多种,比如90分位、95分位、75分位。以90分位为例,我们把这段时间请求的响应时间从小到大排序,假如一共有100个请求,那么排在第90位的响应时间就是90分位值。分位值排除了偶发极慢请求对于数据的影响,能够很好地反应这段时间的性能情况,分位值越大,对于慢请求的影响就越敏感。

分位值是最适合作为时间段内,响应时间统计值来使用的,在实际工作中也应用最多。 平均值也可以作为一个参考值。

通常使用吞吐量或者响应时间来度量并发和流量,使用吞吐量的情况会更多一些。这两个指标是呈倒数关系: 响应时间1s时,吞吐量是每秒1次,响应时间缩短到10ms,那么吞吐量就上升到每秒100次。所以,一般我们度量性能时都会同时兼顾吞吐量和响应时间,比如我们设立性能优化的目标时通常会这样表述:在每秒1万次的请求量下,响应时间99分位值在10ms以下。

那么,响应时间究竟控制在多长时间比较合适呢?

从用户使用体验的角度来看,200ms是第一个分界点:接口的响应时间在200ms之内,用户是感觉不到延迟的,就像是瞬时发生的一样。而1s是另外一个分界点:接口的响应时间在1s之内时,虽然用户可以感受到一些延迟,但却是可以接受的,超过1s之后用户就会有明显等待的感觉,等待时间越长,用户的使用体验就越差。所以,健康系统的99分位值的响应时间通常需要控制在200ms之内,而不超过1s的请求占比要在99.99%以上。

现在你了解了性能的度量指标,那我们再来看一看,随着并发的增长我们实现高性能的思路是怎样的。

4 性能优化

假如说,你现在有一个系统,这个系统中处理核心只有一个,执行的任务的响应时间都在10ms,它的吞吐量是在每秒100次。那么我们如何来优化性能从而提高系统的并发能力呢?主要有两种思路:一种是提高系统的处理核心数,另一种是减少单次任务的响应时间。

提高系统的处理核心数

提高系统的处理核心数就是增加系统的并行处理能力。 比如可以把系统的处理核心数增加为两个,并且增加一个进程,让这两个进程跑在不同的核心上。这样从理论上,你系统的吞吐量可以增加一倍。 这种情况下,吞吐量和响应时间就不是倒数关系了,而是:

代码语言:javascript复制
吞吐量=并发进程数/响应时间

计算机领域的阿姆达尔定律(Amdahl’s law)是吉恩·阿姆达尔在1967年提出的。它描述了并发进程数与响应时间之间的关系,含义是在固定负载下,并行计算的加速比,也就是并行化之后效率提升情况,可以用下面公式来表示:

代码语言:javascript复制
(Ws   Wp) / (Ws   Wp/s)
  • Ws表示任务中的串行计算量
  • Wp表示任务中的并行计算量
  • s表示并行进程数

可推出另外一个公式:

代码语言:javascript复制
1/(1-p p/s)
  • s还是表示并行进程数
  • p表示任务中并行部分的占比 当p为1时,也就是完全并行时,加速比与并行进程数相等;当p为0时,即完全串行时,加速比为1,也就是说完全无加速;当s趋近于无穷大的时候,加速比就等于1/(1-p),你可以看到它完全和p成正比。特别是,当p为1时,加速比趋近于无穷大。

我们似乎找到了解决问题的银弹,无限制地增加处理核心数就能无限制地提升性能? 随并发进程数的增加,并行的任务对于系统资源的争抢也会愈发严重。在某一个临界点上继续增加并发进程数,反而会造成系统性能的下降,这就是性能测试中的拐点模型。

  • 并发用户数处于轻压力区时,响应时间平稳,吞吐量和并发用户数线性相关
  • 并发用户数处于重压力区时,系统资源利用率到达极限,吞吐量开始有下降的趋势,响应时间也会略有上升。这个时候,再对系统增加压力,系统就进入拐点区,处于超负荷状态,吞吐量下降,响应时间大幅度上升。

所以评估系统性能时通常需要做压测,找到系统的“拐点”,从而知道系统的承载能力,也便于找到系统瓶颈,持续优化系统性能。

减少单次任务响应时间

首先看你的系统是CPU密集型还是IO密集型的,不同类型的系统性能优化方式不尽相同。

CPU密集型系统中,需要处理大量的CPU运算,那么选用更高效的算法或者减少运算次数就是这类系统重要的优化手段。比方说,如果系统的主要任务是计算Hash值,那么这时选用更高性能的Hash算法就可以大大提升系统的性能。发现这类问题的主要方式,是通过一些Profile工具来找到消耗CPU时间最多的方法或者模块,比如Linux的perf、eBPF等。

IO密集型系统指的是系统的大部分操作是在等待IO完成:

  • 磁盘IO
  • 网络IO

大部分都属于IO密集型,比如数据库系统、缓存系统、Web系统。这类系统的性能瓶颈可能出在系统内部,也可能是依赖的其他系统,而发现这类性能瓶颈的手段主要有两类:

采用工具

Linux的工具集很丰富,完全可以满足你的优化需要,比如网络协议栈、网卡、磁盘、文件系统、内存,等等。这些工具的用法很多,你可以在排查问题的过程中逐渐积累。除此之外呢,一些开发语言还有针对语言特性的分析工具,比如说Java语言就有其专属的内存分析工具。

监控来发现性能问题

在监控中我们可以对任务的每一个步骤做分时的统计,从而找到任务的哪一步消耗了更多的时间。

找到了系统瓶颈,如何优化呢? 如果是数据库访问慢,那么就要看是不是有锁表的情况、是不是有全表扫描、索引加得是否合适、是否有JOIN操作、需不需要加缓存 如果是网络的问题,就要看网络的参数是否有优化的空间,抓包来看是否有大量的超时重传,网卡是否有大量丢包等。

比如做广告检索遇到的问题,倒排索引存在Redis,每次都要请求Redis,但是并发时,Redis连接数太大,甚至打开文件数过大,后采用Redis连接池,Redis连接数得到控制,而且响应更快,后来随着并发数的增大,连接池资源耗尽,而且Redis也有并发限制,数据传输导致大量占用带宽,响应时间更久,因此,又使用了本地缓存,每次请求先请求本地缓存,找不到再请求Redis,缓存到本地,缓存更新时通过消息队列来通知程序更新本地缓存,这样节省了大量的和Redis之间的请求耗时和带宽占用,性能有了数倍的提升。

总结

高并发:高性能(响应时间)、高可用(down机、故障、维护)、可扩展(应急扩容) 响应时间(平均值、最大值、分位值),响应为1s,吞吐量为每秒1次,响应缩短到10ms,吞吐量上升到每秒100次,从用户体验来说:200ms分界点,1s为另一个分界点,健康系统的99分位值的响应时间控制在200ms以内,不超过1s的请求占比要超过99.99% 高并发下的性能优化手段: 1.提高系统的处理核心数(吞吐量=核心数(并发进程数)/响应时间(s)) 但并非无限增加核心数就可以增加吞吐量,随着进程数增加,并行的任务对于资源的争夺也增加,在某 个临界点,进程增加导致系统的性能下降,这就是性能测试中的拐点模型,所以在评估系统性能时,需要做压力测试,找到拐点 2.减少单次任务响应时间 cpu密集型:优化算法 io密集型:1.采用工具,linux的工具集 2.通过监控,对任务的每一个步骤做分时统计,从而找到任务中哪一步小号消耗了更多的时间

0 人点赞