You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
12 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 16 | 高性能和可伸缩架构:业务增长,能不能加台机器就搞定?
你好,我是王庆友,今天我来和你聊一聊如何打造高性能和可伸缩的系统。
在课程的[第11讲](https://time.geekbang.org/column/article/212066),我和你介绍了,技术架构除了要保证系统的高可用,还要保证系统的高性能和可伸缩,并且能以低成本的方式落地。在实践中呢,高性能、可伸缩和低成本紧密相关,处理的手段也比较类似,这里我就放在一起来给你讲解。
在实际的工作当中,我们一般会比较关注业务功能的实现,而很少关注系统的性能,所以我们经常会面临以下这些挑战:
* **系统的TPS很低只要流量一大系统就挂加机器也没用**
* **机器的资源利用率很低,造成资源严重浪费。**
我曾经就统计过公司云服务器的资源利用率结果让我非常意外有相当比例的服务器它们的CPU和内存平均利用率长期不到1%但与此同时这些系统整体的TPS只有个位数。这里你可以发现资源利用率低和系统性能低的现象同时并存很显然系统没有充分利用硬件资源它的性能有很大的优化空间。
所以今天,我就先来给你介绍一下常用的性能数据,让你建立起对性能的基本概念;然后,我会和你具体讲解实现系统高性能和可伸缩的策略,让你能在实践中灵活运用。
## 常用的性能数据
对于服务器来说1ms的时间其实不算短它可以做很多事情我在这里列了几个基础的性能数据你可以把它们看做是系统性能的基线。
![](https://static001.geekbang.org/resource/image/7f/83/7fb77d02fc24085534617eab95e2e283.jpg)
你可以看到内存的数据读取是SSD磁盘的10倍SSD磁盘又是普通磁盘的10倍一个远程调用的网络耗时是机房内部调用的1000倍一个分布式缓存访问相对于数据库访问性能也有数十倍的提升。
了解了这些常用的性能数据你就能对性能建立一个直观的认识有些时候我们采取一些简单的手段就能提升系统的性能。比如说如果磁盘的IO访问是瓶颈我们只要用SSD磁盘来代替机械硬盘就能够大幅度地提升系统的性能。
## 高性能的策略和手段
那么对于一个实际的业务系统来说情况就会复杂很多。一个外部请求进来需要经过内部很多的软硬件节点处理用户请求的处理时间就等于所有节点的处理时间相加。只要某个节点性能有问题比如数据库或者某项资源不足比如网络带宽系统整体的TPS就上不去。这也是在实践中很多系统TPS只有个位数的原因。
不同类型的节点,提升性能的方法是不一样的,概括起来,总体上可以分为三类。
### 加快单个请求处理
这个其实很好理解。简单来说,就是当一个外部请求进来,我们要让系统在最短的时间内完成请求的处理,这样在单位时间内,系统就可以处理更多的请求。具体的做法主要有两种:
1. **优化处理路径上每个节点的处理速度。**比如说,我们可以在代码中使用更好的算法和数据结构,来降低算法的时间和空间复杂度;可以通过索引,来优化数据库查询;也可以在高读写比的场景下,通过缓存来代替数据库访问等等。
2. **并行处理单个请求。**我们把一个请求分解为多个子请求内部使用多个节点同时处理子请求然后对结果进行合并。典型的例子就是MapReduce思想这在大数据领域有很多实际的应用。
### 同时处理多个请求
当有多个外部请求进来时,系统同时使用多个节点来处理请求,每个节点分别来处理一个请求,从而提升系统单位时间内处理请求的数量。
比如说我们可以部署多个Web应用实例由负载均衡把外部请求转发到某个Web实例进行处理。这样如果我们有n个Web实例在单位时间里系统就可以处理n倍数量的请求。
除此之外,在同一个节点内部,我们还可以利用多进程、多线程技术,同时处理多个请求。
### 请求处理异步化
系统处理请求不一定要实时同步,请求流量的高峰期时间往往很短,所以有些时候,我们可以延长系统的处理时间,只要在一个相对合理的时间内,系统能够处理完请求就可以了,这是一种异步化的处理方式。
典型的例子呢,就是通过消息系统对流量进行削峰,系统先把请求存起来,然后再在后台慢慢处理。
我们在处理核心业务时,把相对不核心的逻辑做异步化处理,也是这个思路。比如说下单时,系统实时进行扣库存、生成订单等操作,而非核心的下单送积分、下单成功发消息等操作,我们就可以做异步处理,这样就能够提升下单接口的性能。
**那么,在实践中,我们应该使用哪种方式来保障系统的高性能呢?**答案是,我们需要根据实际情况,把三种手段结合起来。
首先,我们要加快单个请求的处理。单节点性能提升是系统整体处理能力提升的基础,这也是我们作为技术人员的基本功。**但这里的问题是,节点的性能提升是有瓶颈的**我们不能超越前面说的基础操作的性能。至于把请求分解为多个小请求进行并行处理这个在很多情况下并不可行我们知道MapReduce也有使用场景的限制。
对多个请求进行同时处理是应对海量请求的强有力手段,如果我们能够水平扩展每一个处理节点,这样在理论上,系统处理请求的能力无限的。**而这里的问题是**对于无状态的计算节点我们很容易扩展比如说Web应用和服务但对于有状态的存储节点比如说数据库要想水平扩展它的处理能力我们往往要对系统做很大的改造。
至于异步化处理,在某些场景下是很好的提升系统性能的方式,我们不用增加机器,系统就能够完成请求的处理。**但问题是**,同步调用变成异步的方式,往往会导致处理结果不能实时返回,有时候会影响到用户体验,而且对程序的改造也会比较大。
所以,我们在考虑系统高性能保障的时候,首先需要考虑提升单个请求的处理速度,然后再考虑多请求的并发处理,最后通过异步化,为系统争取更长的处理时间。
具体的处理手段,根据业务场景的不同,我们需要做综合考虑,在满足业务的基础上,争取对系统改造小,总体成本低。
## 可伸缩的策略和手段
我们经常说,业务是可运营的,而实际上,系统也是可运营的。我们可以动态地调整系统软硬件部署,在业务高峰期增加软硬件节点,在业务低谷期减少软硬件节点,这就是系统的可伸缩能力。
系统的可伸缩也有两种实现方式。
### 第一个是节点级别的可伸缩
**对于无状态的节点,我们直接增减节点就可以了**。比如说订单服务白天我们需要10台机器来提供服务到了半夜由于单量减少我们就可以停掉部分机器。
如果做得好,我们还可以实现**弹性伸缩**让系统根据硬件的负载情况来确定机器的数量。比如说当服务器的CPU或内存使用率在10%以下了,系统就自动减少服务实例的数量。
**而对于有状态的服务,我们需要能够支持状态数据的重新分布。**比如进行水平分库的时候要从4个库增加到8个库我们需要把原先4个库的数据按照新的分库规则重新分布到8个库中。如果这个调整对应用的影响小那系统的可伸缩性就高。
### 第二个是系统级别的可伸缩
我们知道,系统是一个整体,如果只是节点级别的伸缩,我们可能要对多个节点分别进行操作,而且不同节点的资源配置会相互影响,这样对各个节点的调整就非常复杂,影响了系统的可伸缩能力。**如果能实现系统端到端的伸缩,同时对多个节点进行伸缩处理,那系统的可伸缩能力就更高了。**
所以这里,我们可以把多个处理节点打包在一起,形成一个处理单元。
举个例子针对交易场景我们可以把商品浏览、加购物车、下单、支付这几个节点放一起形成一个逻辑上的单元在单元内部形成调用的闭环。具体使用的时候我们可以按照用户维度来划分交易单元。比如说让交易单元A处理用户ID对2取模为0的用户下单流程交易单元B处理用户ID对2取模为1的用户下单流程。
这样我们对一个整体的交易单元进行扩容或者缩容每增加一个交易单元就意味着同时增加商品浏览、加购物车、下单、支付4个节点这4个节点的处理能力是匹配的。你可以参考下面的这张交易单元化的示意图
![](https://static001.geekbang.org/resource/image/d6/54/d655e5fd2ff1c4c72dfae0779aa9ca54.jpg)
**通过单元化处理,我们把相关的节点绑定在一起,同进同退,更容易实现系统的可伸缩。**
而如果我们把单元扩大到系统的所有节点,这就是一个**虚拟机房**的概念。我们可以在一个物理机房部署多个虚拟机房,也可以在不同的物理机房部署多个虚拟机房,这样,部署系统就像部署一个应用一样,系统的可伸缩性自然就更好。
## 高性能和可伸缩架构原则
说完了高性能和可伸缩的策略,接下来,我再说下具体的架构设计原则,让你能够在实践中更好地落地。
* 可水平拆分和无状态
这意味着节点支持多实例部署,我们可以通过水平扩展,线性地提升节点的处理能力,保证良好的伸缩性以及低成本。
* 短事务和柔性事务
短事务意味着资源锁定的时间短,系统能够更好地支持并发处理;柔性事务意味着系统只需要保证状态的最终一致,这样我们就有更多的灵活手段来支持系统的高性能,比如说通过异步消息等等。
* 数据可缓存
缓存是系统性能优化的利器如果数据能够缓存我们就可以在内存里拿到数据而不是通过磁盘IO这样可以大大减少数据库的压力相对于数据库的成本缓存的成本显然也更低。
* 计算可并行
如果计算可并行我们就可以通过增加机器节点加快单次请求的速度提高性能。Hadoop对大数据的处理就是一个很好的例子。
* 可异步处理
异步处理给系统的处理增加了弹性空间,我们可以利用更多的处理时间,来降低系统对资源的实时需求,在保证系统处理能力的同时,降低系统的成本。
* 虚拟化和容器化
虚拟化和容器化是指对基础资源进行了抽象这意味着我们不需要再依赖具体的硬件对节点的移植和扩容也就更加方便。同时虚拟化和容器化对系统的资源切分得更细也就说明对资源的利用率更高系统的成本也就更低。举个例子我们可以为单个Docker容器分配0.1个CPU当容器的处理能力不足时我们可以给它分配更多的CPU或者增加Docker容器的数量从而实现系统的弹性扩容。
实现了系统的高性能和可伸缩,就表明我们已经最大限度地利用了机器资源,那么低成本就是自然的结果了。
## 总结
在课程的开始,我给出了一些基础的性能指标,在具体的业务场景中,你就可以参考这些指标,来评估你当前系统的性能。
另外,我还分别针对系统的高性能和可伸缩目标,介绍了它们的实现策略和设计原则,在工作中,你可以根据具体的业务,由易到难,采取合适的手段来实现这些目标。
在实践中呢,实现高可用和可伸缩的手段也是多种多样的,接下来的课程中,我还会通过实际的案例,来具体说明实现这些目标的有效手段,帮助你更好地落地。
**最后,给你留一道思考题**:在工作中,你都采取过哪些手段保证了系统的高性能和可伸缩呢?
欢迎在留言区和我互动,我会第一时间给你反馈。如果这节课对你有帮助,也欢迎你把它分享给你的朋友。感谢阅读,我们下期再见。