系统设计学习(一) 如何提升系统性能?

系统设计学习(一) 如何提升系统性能?

一、前言

性能反应了系统的使用体验,两个系统QPS都是1w,如果第一个响应是毫秒级别,第二个响应是秒级别,它们带给用户的体验肯定是不同的。

二、性能优化原则

想实现一个功能,就要了解这个功能的原则。

**首先,性能优化一定不能盲目,一定是问题导向的。**脱离了问题,盲目地提早优化会增加系统的复杂度,浪费开发人员的时间,并且也因为某些优化可能对业务上有些折中的考虑,所以也会损伤业务。

**其次,性能优化也遵循“八二原则”,**即你可以用20%的精力解决80%的性能问题。所以我们再优化过程中一定要抓住主要矛盾,优先优化主要的性能瓶颈点。

**再次,性能优化也要有数据支撑。**再优化过程中,你要时刻了解你的优化让响应时间减少了多少,提升了多少的吞吐量。

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

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

三、性能度量指标

​ 性能优化的第三点原则中提到,对于性能我们需要有度量的标准,有了数据才能明确目前存 在的性能问题,也能够用数据来评估性能优化的效果。所以明确性能的度量指标十分重要。

一般来说,度量性能的指标是系统接口的响应时间,但是单次的响应时间是没有意义的,你 需要知道一段时间的性能情况是什么样的。所以,我们需要收集这段时间的响应时间数据, 然后依据一些统计方法计算出特征值,这些特征值就能够代表这段时间的性能情况。我们常 见的特征值有以下几类。

平均值

顾名思义,平均值是把这段时间所有请求的响应时间数据相加,再除以总请求数。平均值可 以在一定程度上反应这段时间的性能,但它敏感度比较差,如果这段时间有少量慢请求时, 在平均值上并不能如实的反应。

举个例子,假设我们在 30s 内有 10000 次请求,每次请求的响应时间都是 1ms,那么这 段时间响应时间平均值也是 1ms。这时,当其中 100 次请求的响应时间变成了 100ms, 那么整体的响应时间是 (100 * 100 + 9900 * 1) / 10000 = 1.99ms。你看,虽然从平均值 上来看仅仅增加了不到 1ms,但是实际情况是有 1% 的请求(100/10000)的响应时间已 经增加了 100 倍。所以,平均值对于度量性能来说只能作为一个参考。

最大值

这个更好理解,就是这段时间内所有请求响应时间最长的值,但它的问题又在于过于敏感 了。

还拿上面的例子来说,如果 10000 次请求中只有一次请求的响应时间达到 100ms,那么这 段时间请求的响应耗时的最大值就是 100ms,性能损耗为原先的百分之一,这种说法明显 是不准确的。

分位值

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

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

我在上面提到,脱离了并发来谈性能是没有意义的,我们通常使用吞吐量或者同时在线用户 数来度量并发和流量,使用吞吐量的情况会更多一些。但是你要知道,这两个指标是呈倒数 关系的。

这很好理解,响应时间 1s 时,吞吐量是每秒 1 次,响应时间缩短到 10ms,那么吞吐量就 上升到每秒 100 次。所以,一般我们度量性能时都会同时兼顾吞吐量和响应时间,比如我 们设立性能优化的目标时通常会这样表述:在每秒 1 万次的请求量下,响应时间 99 分位值 在 10ms 以下。

那么,响应时间究竟控制在多长时间比较合适呢?这个不能一概而论。

从用户使用体验的角度来看**,200ms 是第一个分界点**:接口的响应时间在 200ms 之内, 用户是感觉不到延迟的,就像是瞬时发生的一样。而 1s 是另外一个分界点:接口的响应时 间在 1s 之内时,虽然用户可以感受到一些延迟,但却是可以接受的,超过 1s 之后用户就 会有明显等待的感觉,等待时间越长,用户的使用体验就越差。所以,健康系统的 99 分位 值的响应时间通常需要控制在 200ms 之内,而不超过 1s 的请求占比要在 99.99% 以上。
现在你了解了性能的度量指标,那我们再来看一看,随着并发的增长我们实现高性能的思路 是怎样的。

四、高并发下的性能优化

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

1、提高系统的处理核心数

提高系统的处理核心数就是增加系统的并行处理能力,这个思路是优化性能最简单的途径。 拿上一个例子来说,你可以把系统的处理核心数增加为两个,并且增加一个进程,让这两个 进程跑在不同的核心上。这样从理论上,你系统的吞吐量可以增加一倍。当然了,在这种情 况下,吞吐量和响应时间就不是倒数关系了,而是:吞吐量 = 并发进程数 / 响应时间。

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

在这里插入图片描述

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

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

2、减少单次任务响应时间

想要减少任务的响应时间,首先要看你的系统是 CPU 密集型还是 IO 密集型的,因为不同 类型的系统性能优化方式不尽相同。

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

IO 密集型系统指的是系统的大部分操作是在等待 IO 完成,这里 IO 指的是磁盘 IO 和网络 IO。我们熟知的系统大部分都属于 IO 密集型,比如数据库系统、缓存系统、Web 系统。 这类系统的性能瓶颈可能出在系统内部,也可能是依赖的其他系统,而发现这类性能瓶颈的 手段主要有两类。

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

另外一类手段就是可以通过监控来发现性能问题。在监控中我们可以对任务的每一个步骤做 分时的统计,从而找到任务的哪一步消耗了更多的时间。这一部分在演进篇中会有专门的介 绍,这里就不再展开了。

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

总而言之,“兵来将挡水来土掩”,我们需要制定不同的性能优化方案来应对不同的性能问题。

看网络的 参数是否有优化的空间,抓包来看是否有大量的超时重传,网卡是否有大量丢包等。

总而言之,“兵来将挡水来土掩”,我们需要制定不同的性能优化方案来应对不同的性能问题。

3、高性能的实践方案

  1. 集群部署,通过负载均衡减轻单机压力(DNS机房负载、LVS负载、nginx负载)。
  2. 多级缓存,包括静态资源的CDN、本地缓存(带状态了就)、分布式缓存(可以无状态),这里就会出现热点key的缓存穿透、缓存雪崩和缓存数据一致性的问题。
  3. 分库分表和索引优化,以及借助搜索引擎解决复杂查询问题。(纵向分库,和横向分表,索引包括使用explain来优化,以及利用好覆盖索引等),如对于海量文件存储,可通过分布式文件系统HDFS解决,对于key value类型的数据,可通过HBase和Redis等方案解决。对于全文检索场景,可通过搜索引擎如ElasticSearch解决,对于多维分析场景,可通过Kylin或Druid等方案解决。
  4. 异步化,将次要流程通过多线程、MQ、甚至延时任务异步处理。(经典发邮件问题,还有up自己项目中比如一个定时发送接口,可以先入库,再后台起定时任务异步完成、异步组织树)
  5. 限流,需要考虑业务是否运行限流(秒杀、还有评论系统),包括前端限流、Nginx接入层的限流、服务端的限流。
  6. 流量削峰。通过MQ承接流量,再异步处理。
  7. 并发处理,通过多线程将串行逻辑并行化(批量下载图片、批量查询大规模数据)。
  8. 预处理。也就说策略定死的场景数据可以提前缓存,没必要通过后台访问数据库再计算,直接缓存返回给用户,减少数据库和后台压力。
  9. 缓存预热,通过异步任务提前预热数据到本地缓存或者分布式缓存。
  10. 减少io次数,比如数据库和缓存的批量读写、RPC的批量支持。
  11. 减少io时的数据包大小,包括采用轻量级的通讯协议(RPC代替http)、合适的数据结构、去掉接口中的多余字段,减少缓存key的待下,压缩缓存value等。
  12. 程序逻辑优化,比如将大概率阻断执行流程的判断逻辑前置、For循环的计算逻辑优化,或者采用更高效的算法(尽量采用空间换时间,N方的算法变成logN)。
  13. 尽量采用池化技术,包括http请求池、线程池、数据库链接池和Redis连接池。
  14. JVM的优化,JVM算法选型,参数优化。
  15. 锁的选择。
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值