<audio id="audio" title="16 | 高性能和可伸缩架构：业务增长，能不能加台机器就搞定？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/39/3b/39bf384157fe8735071dba40db77573b.mp3"></audio>

你好，我是王庆友，今天我来和你聊一聊如何打造高性能和可伸缩的系统。

在课程的[第11讲](https://time.geekbang.org/column/article/212066)，我和你介绍了，技术架构除了要保证系统的高可用，还要保证系统的高性能和可伸缩，并且能以低成本的方式落地。在实践中呢，高性能、可伸缩和低成本紧密相关，处理的手段也比较类似，这里我就放在一起来给你讲解。

在实际的工作当中，我们一般会比较关注业务功能的实现，而很少关注系统的性能，所以我们经常会面临以下这些挑战：

- **系统的TPS很低，只要流量一大，系统就挂，加机器也没用；**
- **机器的资源利用率很低，造成资源严重浪费。**

我曾经就统计过公司云服务器的资源利用率，结果让我非常意外，有相当比例的服务器，它们的CPU和内存平均利用率长期不到1%，但与此同时，这些系统整体的TPS只有个位数。这里，你可以发现，资源利用率低和系统性能低的现象同时并存，很显然，系统没有充分利用硬件资源，它的性能有很大的优化空间。

所以今天，我就先来给你介绍一下常用的性能数据，让你建立起对性能的基本概念；然后，我会和你具体讲解实现系统高性能和可伸缩的策略，让你能在实践中灵活运用。

## 常用的性能数据

对于服务器来说，1ms的时间其实不算短，它可以做很多事情，我在这里列了几个基础的性能数据，你可以把它们看做是系统性能的基线。

<img src="https://static001.geekbang.org/resource/image/7f/83/7fb77d02fc24085534617eab95e2e283.jpg" alt="">

你可以看到，内存的数据读取是SSD磁盘的10倍，SSD磁盘又是普通磁盘的10倍，一个远程调用的网络耗时是机房内部调用的1000倍，一个分布式缓存访问相对于数据库访问，性能也有数十倍的提升。

了解了这些常用的性能数据，你就能对性能建立一个直观的认识，有些时候，我们采取一些简单的手段就能提升系统的性能。比如说，如果磁盘的IO访问是瓶颈，我们只要用SSD磁盘来代替机械硬盘，就能够大幅度地提升系统的性能。

## 高性能的策略和手段

那么对于一个实际的业务系统来说，情况就会复杂很多。一个外部请求进来，需要经过内部很多的软硬件节点处理，用户请求的处理时间就等于所有节点的处理时间相加。只要某个节点性能有问题（比如数据库），或者某项资源不足（比如网络带宽），系统整体的TPS就上不去。这也是在实践中，很多系统TPS只有个位数的原因。

不同类型的节点，提升性能的方法是不一样的，概括起来，总体上可以分为三类。

### 加快单个请求处理

这个其实很好理解。简单来说，就是当一个外部请求进来，我们要让系统在最短的时间内完成请求的处理，这样在单位时间内，系统就可以处理更多的请求。具体的做法主要有两种：

1. **优化处理路径上每个节点的处理速度。**比如说，我们可以在代码中使用更好的算法和数据结构，来降低算法的时间和空间复杂度；可以通过索引，来优化数据库查询；也可以在高读写比的场景下，通过缓存来代替数据库访问等等。
1. **并行处理单个请求。**我们把一个请求分解为多个子请求，内部使用多个节点同时处理子请求，然后对结果进行合并。典型的例子就是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个节点的处理能力是匹配的。你可以参考下面的这张交易单元化的示意图：

<img src="https://static001.geekbang.org/resource/image/d6/54/d655e5fd2ff1c4c72dfae0779aa9ca54.jpg" alt="">

**通过单元化处理，我们把相关的节点绑定在一起，同进同退，更容易实现系统的可伸缩。**

而如果我们把单元扩大到系统的所有节点，这就是一个**虚拟机房**的概念。我们可以在一个物理机房部署多个虚拟机房，也可以在不同的物理机房部署多个虚拟机房，这样，部署系统就像部署一个应用一样，系统的可伸缩性自然就更好。

## 高性能和可伸缩架构原则

说完了高性能和可伸缩的策略，接下来，我再说下具体的架构设计原则，让你能够在实践中更好地落地。

- 可水平拆分和无状态

这意味着节点支持多实例部署，我们可以通过水平扩展，线性地提升节点的处理能力，保证良好的伸缩性以及低成本。

- 短事务和柔性事务

短事务意味着资源锁定的时间短，系统能够更好地支持并发处理；柔性事务意味着系统只需要保证状态的最终一致，这样我们就有更多的灵活手段来支持系统的高性能，比如说通过异步消息等等。

- 数据可缓存

缓存是系统性能优化的利器，如果数据能够缓存，我们就可以在内存里拿到数据，而不是通过磁盘IO，这样可以大大减少数据库的压力，相对于数据库的成本，缓存的成本显然也更低。

- 计算可并行

如果计算可并行，我们就可以通过增加机器节点，加快单次请求的速度，提高性能。Hadoop对大数据的处理就是一个很好的例子。

- 可异步处理

异步处理给系统的处理增加了弹性空间，我们可以利用更多的处理时间，来降低系统对资源的实时需求，在保证系统处理能力的同时，降低系统的成本。

- 虚拟化和容器化

虚拟化和容器化是指对基础资源进行了抽象，这意味着我们不需要再依赖具体的硬件，对节点的移植和扩容也就更加方便。同时，虚拟化和容器化对系统的资源切分得更细，也就说明对资源的利用率更高，系统的成本也就更低。举个例子，我们可以为单个Docker容器分配0.1个CPU，当容器的处理能力不足时，我们可以给它分配更多的CPU，或者增加Docker容器的数量，从而实现系统的弹性扩容。

实现了系统的高性能和可伸缩，就表明我们已经最大限度地利用了机器资源，那么低成本就是自然的结果了。

## 总结

在课程的开始，我给出了一些基础的性能指标，在具体的业务场景中，你就可以参考这些指标，来评估你当前系统的性能。

另外，我还分别针对系统的高性能和可伸缩目标，介绍了它们的实现策略和设计原则，在工作中，你可以根据具体的业务，由易到难，采取合适的手段来实现这些目标。

在实践中呢，实现高可用和可伸缩的手段也是多种多样的，接下来的课程中，我还会通过实际的案例，来具体说明实现这些目标的有效手段，帮助你更好地落地。

**最后，给你留一道思考题**：在工作中，你都采取过哪些手段保证了系统的高性能和可伸缩呢？

欢迎在留言区和我互动，我会第一时间给你反馈。如果这节课对你有帮助，也欢迎你把它分享给你的朋友。感谢阅读，我们下期再见。
