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.

303 lines
19 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.

# 34 | 容量规划:如何精准地对生产系统做容量预估?
你好,我是高楼。
到这里啊,我们专栏的主体内容已经讲得差不多了,场景也已经跑起来了。这节课,我们就来聊一聊容量规划。
对于一个线上系统来说,精准地判断系统最大容量是保证系统能稳定运行的前提条件,而对于大部分系统来说,它们的规划和评估都太过粗略,以至于不是浪费了资源就是随时等死,所以容量评估也必然是性能项目要产出的结论之一。这也是全链路线上压测出现的原因。
对于系统级的容量评估,在当前的技术市场中,除了拿硬件怼上去之外,似乎很难找到什么有效的方法。
我知道有些人会说不是有TPC-C、TPC-E…之类的吗不是有基于CPU算力的XX模型吗不管你怎么说在实际的工作场景中大部分的算法也还是下面这样的
> 当一个节点支持100TPS时有100个同样的节点就大概能支持:
> 100 × 100 × 80% ≈ 8000TPS
之所以乘以个80%是因为我们从心理上就觉得实际场景可以支持的TPS总量好像可以但又大概率达不到线性增加的程度因为一定会有性能的损耗。但是这样算来显得也太不专业了一个复杂的系统怎么能用小学生就能理解的公式来表达呢。确实这么简单的公式会忽略很多问题它过于简单粗暴不说也非常不精准。
所以一些架构师在做系统规划的时候会根据TPC-C来计算下面我们就来说一说这种计算方式同时也聊一聊磁盘和网络容量的评估方式。
## TPC-C容量评估
在讲解TPC-C之前我们先来了解一下TPC。TPCTransaction Processing Performance Council的全称是事务处理性能委员会它是一个由数十家会员企业组成的非盈利组织。TPC主要负责制定商务应用基准性能测试的标准规范、性能和价格度量还负责管理测试结果的发布。而所有的发布结果都将由TPC授权的独立审核员审核。
同时为了保证结果的公允TPC还成立了技术咨询委员会TAB。如果有人对TPC发布的结果有疑问他们可以去TAB理论。注意他们只会发布应用程序性能评估的基准性能测试标准规范企业的测试代码是可以自己编写的。
那TPC和 TPC-A、TPC-B、TPC-C、TPC-E有什么关系呢我们其实可以把它们看成是TPC推出的基准性能测试标准规范的不同版本TPC-A、TPC-B是前两个版本TPC-C版本出来之后就迅速替换了前两个版本。
TPC-A1989年发布)主要是对借记卡在局域网和广域网中的性能进行基准评估它增加了对ACID的要求。
TPC-B1991年发布使用了和TPC-A相同的事务类型但它取消了TPC-A中的网络和用户交互组件剩下的是一个批处理事务基准。
TPC-C1992年发布使用的是商品批发销售公司的模型。它主要使用了新订单、支付操作、发货、订单状态查询、库存状态查询这五个事务来执行并发**TPC-C测试的结果主要有两个指标也就是每分钟事务数tpmC和性价比Price/Performance简写为Price/tpmC。**
而TPC-E2007年发布使用的是证券交易所的业务模型它模拟了12种事务类型包括交易查询事务、交易执行事务等。
TPC发布TPC-E的初衷是想让它替代TPC-C的但是从发布到现在十几年了看起来他们并没有得到预期的效果据说只有SQL-Server会刷TPC-E的榜单。
既然有这么多企业去刷TPC-C的榜单那它的指标具体是怎么计算的呢下面我们就来看看。
TPC-C的tpmC指标计算公式一般是下面这样
![](https://static001.geekbang.org/resource/image/89/85/8919d7cdf6cc29e09f24afc25f7a3985.png?wh=1192x175)
我来简单解释一下:
* 日均请求量:后台请求数。
* 请求峰值系数这个值从历史经验数据中得来像有些银行就是2-3之间。
* 请求复杂度这里需要换算一下看看一个请求约为多少个TPC-C的标准请求。
* 预留扩展以用户数增长、请求量增长来预估系统处理能力的增长。没有固定值像银行系统有个3-5倍已经挺高的而互联网系统就不一而足了预估成百上千倍的增长量的大有人在这就取决于资本如何运作了。
* 资源最佳利用率通常这个数据为了好看又为了上下都交得了差会界定在60%-80%左右,没有严格的规定。
根据这个公式,我们代入一组数据来算一下:
![](https://static001.geekbang.org/resource/image/42/55/42780034e4b3d52db6d867481d1e2455.png?wh=1048x229)
上面这些数字代表的内涵跟前面的标准公式是一一对应的,我就不单独解释了。
在标准的TPC-C基准中如果我们在一个或固定的几个机器上部署了一个完整的系统那么通过这个公式我们就可以计算出一个指标值来。
你可能会觉得拿到这么一组数据之后如果我们想要知道这个值需要多少硬件资源就可以拿计算出的值和TPC-C标准表来对照了。别人一个主机硬件的TPC-C值如果是900那么你需要的就是$\\frac{12,500,000}{900}\\approx139$主机。
这个计算过程看起来非常合理,又有指标可以参考,感觉没什么难度。但是你在具体的项目中想要计算的时候,却并没有这么简单。原因主要有下面四点:
首先你的系统的架构部署和标准的测试系统不一样。这是一个很难平衡的事情因为每个业务系统都或多或少有一些独特设计这些根据业务特性所做的部署架构设计会和TPC-C的跑分系统有差异所以我们不能直接用TPC-C的跑分值来做参考。
其次请求复杂度在大部分系统中都是比较难计算的。你可能觉得可以“使用O(n)复杂度表示法”但是这种表示法在代码方法级别是容易计算的而到了微服务分布式系统中由于服务拆分较多导致了服务间调用开始变多O(n)就很难表达出架构级的复杂度了。
第三资源最佳利用率很难匹配到生产系统。根据我的经验在生产环境中很多企业投入的硬件资源都远远高于所谓的最佳利用率的值。银行系统不用说为了保证稳定性系统资源利用率超过15%的时候都非常少。而互联网大厂也只有在一两个业务峰点的资源利用率会高,但为了保证系统在业务峰点能够正常运行,互联网大厂也几乎是百倍千倍地提前准备冗余的资源。
第四峰值系数也是很难确定的。一方面生产数据拿到的只能是历史数据要想用这个数据来计算未来的需求只有一种方法能让计算结果准确那就是业务模型和业务量都不发生大的变化但这是几乎没法预测和保障的。另一方面企业在发展业务也会发生变化这时候如果仍然用历史数据来计算tpmC也会不准确。
鉴于这些原因虽然TPC-C的基准测试是标准的基准测试中的数据也是准确的但到了真实的业务系统中这个值却只能用来参考。
如果TPC-C提供的逻辑不是那么精准。那怎么办呢我们有没有一种更为精准的方式呢下面我就来聊一聊我认为更为精准的容量评估方式排队论。
## 排队论容量评估
因为排队论完整讲起来需要很多的数学理论知识再开一个新专栏都不够讲的所以这里我只是给你提供一个思考的方向和实际落地的demo实现。
什么是排队论呢?
> 排队论 (queuing theory) ,是研究系统随机聚散现象和随机服务系统工作过程的数学理论和方法,又称随机服务系统理论,为运筹学的一个分支。
排队论的模型表示为X/Y/Z/A/B/C。其中
> X到达时间分布
> Y服务时间分布
> Z线程数
> A系统容量限制
> B请求源数目
> C服务规则。
通常我们认为一个系统的系统容量限制和请求源数目也就是A和B都是无限大的因为系统是可以一直提供服务的。那C呢如果是请求-响应式的同步系统我们可以认为是先进先出的服务规则如果是异步情况就可以考虑平均的服务规则。Z是服务线程数这个比较容易理解。但是这里我要说明一下当我们使用排队论算一个服务时Z可以看作是服务中的线程数但如果我们想用排队论来看整个微服务分布式架构的容量能力时应该把Z看作是并行的服务节点个数。X可以看作TPSY可以看成是响应时间。
这样一来我们就可以把上面的逻辑运用在具体的项目计算过程中了。下面我们就结合demo代码来看一下计算逻辑。
这里我用R语言来实现因为在R语言中有一个queuecomputer的开源项目里面实现了一些基本的算法。下面我给出了实现这个 demo 的代码,代码相应的解释,我也都写到了每行的后面。
```java
library(queuecomputer)
set.seed(4) #定义随机数种子
NumberOfRequests <- 40000000 #定义请求数
lambda_a <- 200/1 #到达率
lambda_s <- 167/1 #服务率
interarrivals <- rexp(NumberOfRequests, lambda_a) #用户或请求呈指数分布
arrivals <- cumsum(interarrivals) #用户或请求总数
service <- rexp(NumberOfRequests, lambda_s) #用户或请求得到的服务呈指数分布
# 服务器个数(也可称为服务线程个数,取决于模型创建在哪个Leverl
NumberOfServers = 5
QueuingServerN <- queue_step(arrivals = arrivals, service = service, servers = NumberOfServers) #用到达时间和服务时间来计算响应时间和队列长度
QueuingServerN
QueuingServerN_summary <- summary(QueuingServerN) # 计算队列的摘要数据
QueuingServerN_summary # 打印摘要数据
QueuingServerN_summary$slength_sum # 总队列长度
QueuingServerN_summary$qlength_mean # 平均队列长度
QueuingServerN_summary$slength_mean # 平均请求长度(系统中的请求个数)
QueuingServerN_summary$mwt # 系统级平均等待时间
QueuingServerN_summary$mrt # 系统级平均响应时间
```
运行这段代码会得到下面的结果(部分内容):
```java
..................
> QueuingServerN_summary
Total customers:
40000000
Missed customers:
0
Mean waiting time:
1.28e-05
Mean response time:
0.006
Utilization factor:
0.239512129307182
Mean queue length:
0.00256
Mean number of customers in system:
1.2
> QueuingServerN_summary$slength_sum
# A tibble: 17 x 2
queuelength proportion
<int> <dbl>
1 0 0.302
2 1 0.361
3 2 0.216
4 3 0.0864
5 4 0.0259
6 5 0.00617
7 6 0.00147
8 7 0.000353
9 8 0.0000854
10 9 0.0000213
11 10 0.00000489
12 11 0.00000126
13 12 0.000000230
14 13 0.0000000998
15 14 0.0000000831
16 15 0.0000000358
17 16 0.00000000722
> # 平均队列长度
> QueuingServerN_summary$qlength_mean
[1] 0.002556998
> # 平均请求长度(系统中的请求个数)
> QueuingServerN_summary$slength_mean
[1] 1.200118
> QueuingServerN_summary$mwt # 系统级平均等待时间
[1] 1.27839e-05
> QueuingServerN_summary$mrt # 系统级平均响应时间
[1] 0.006000121
```
从这个结果来看我们总共有四千万客户请求没有失败的请求。因为我们的服务率是167而到达率为200所以有一定的等待还好这个等待时间并不长只有1.27839e-05而平均的系统响应时间为0.006000121也就是6毫秒左右。从这个数据来看支持四千万的总请求量在到达率为200服务率为167的情况下就需要1.27839e-05+0.006的总时间,把这个时间和你的业务指标相对比,就可以判断出是否满足业务需求了。
排队论这个逻辑也可以用到生产环境中,前提就是要获得请求数、服务线程个数以及请求到达的分布函数、响应时间的分布函数,这些数据都可以通过日志获得。如果你再发散一下思维,还可以把这个逻辑用做实时计算。比方说,在大压力的场景下,如果我们想把响应时间降到指标以下,可以用它来计算服务线程数。
除了对性能峰值的综合评估(上面我们说的两种方式),磁盘和网络资源也需要进行评估。
## 磁盘容量评估
我们先来看看磁盘的容量评估。
对于应用程序来说,本地保存日志一般都是循环的,循环策略可以根据文件个数、文件大小、保存时间进行设置,所以相对来说磁盘容量是容易计算的。
而对于数据库这样需要长期保存数据的地方来说,想要计算磁盘容量就要多加注意了。通常我们会创建一个这样的公式来进行计算:
$$磁盘容量=原始磁盘容量+\\sum\_{r=1}^{单表}(记录长度\\times记录数\\times保存期限\\times数据库膨胀因子\\times备份因子$$
* 数据库膨胀因子:要根据具体的业务量进行评估。不同的业务模型,它们数据库的膨胀速度会有很大差别,而且不同的表的膨胀因子也不一样。
* 备份因子:数据库最怕丢失数据,所以备份是必须的,至于要备份几份就得看策略了。有很多重要的系统常常会有至少三个备份,分别存在本地机房、本城机房和远程机房。
有了这些数据之后,再计算总体需要的磁盘容量空间就比较容易了。但这个公式看似简单,如果你想把所有库的所有表都用这个公式计算一遍,其实也很不容易。
不过像我们这个专栏的示例项目,因为搭建得比大企业简单很多,所以数据上也会少很多。下面我们就来看看,我们的项目如果跑一分钟会增加多少数据。
我们先用下面两个语句分别查询数据库表空间和库空间大小。
* 查询Mall库的所有表空间大小。
```java
SELECT
TABLE_NAME AS '表名',
CONCAT(ROUND(TABLE_ROWS / 10000, 2), ' 万行') AS '行数',
CONCAT(ROUND(DATA_LENGTH / (1024 * 1024), 2),
'MB') AS '表空间',
CONCAT(ROUND(INDEX_LENGTH / (1024 * 1024), 2),
' MB') AS '索引空间',
CONCAT(ROUND((DATA_LENGTH + INDEX_LENGTH) / (1024 * 1024),
2),
' MB') AS '总空间'
FROM
information_schema.TABLES
WHERE
TABLE_SCHEMA = 'mall'
ORDER BY TABLE_ROWS DESC;
```
* 查询整库空间大小。
```java
SELECT
TABLE_SCHEMA,
CONCAT(TRUNCATE(SUM(data_length) / 1024 / 1024,
2),
' MB') AS data_size,
CONCAT(TRUNCATE(SUM(index_length) / 1024 / 1024,
2),
'MB') AS index_size
FROM
information_schema.tables
GROUP BY TABLE_SCHEMA
ORDER BY data_length DESC;
```
运行之前:
![图片](https://static001.geekbang.org/resource/image/27/79/27d6d2ca25d7ce0496f5372db9989679.png?wh=872x670)
![图片](https://static001.geekbang.org/resource/image/dc/ca/dc34512a061yyfb99bf50f753459c8ca.png?wh=518x224)
满负载运行一分钟后:
![图片](https://static001.geekbang.org/resource/image/0c/15/0ce6dfe30e18ff62c34e4daf0e973415.png?wh=876x704)
![图片](https://static001.geekbang.org/resource/image/d0/17/d02c5ded9e08000904e673f2f675e517.png?wh=508x218)
从Mall库的整体大小来计算数据库表空间一分钟增加了3.87M。如果是一天呢那就是近5.4G。这只是数据库空间的增加,像日志之类的还要另算。
到这里,磁盘容量的评估就完成了。这样根据实际的测试过程计算出的结果是比较靠谱的。
## 网络容量评估
接下来我们还要算一下网络容量。
要想计算网络容量首先得知道一个系统稳定运行的整体最大TPS。当然了要想知道这个TPS峰值你得先保证我们前面讲过的容量场景已经执行通过了。
因为容量场景已经包括了像业务模型、测试数据、测试环境等等的前提条件所以当我们得到了系统稳定运行的最大TPS之后就可以直接用下面这个公式进行计算了
$$ 上行带宽 = 最大TPS\\times 每秒上行数据大小$$
$$ 下行带宽 = 最大TPS\\times 每秒下行数据大小$$
为什么要分为上下行带宽呢?主要是因为在 ISP 运营商提供的网络服务中上下行带宽的区别很大。像平时我们家里的宽带工作人员非常自信地跟你说接入之后就能专享100M的带宽实际上完全达不到这个值。
家里带宽不够最多也就是卡得你一个人不舒服,但企业级的带宽不够,那就是严重的生产事故了。我们再对着系统架构图来看一下有哪些带宽是需要计算的。
![图片](https://static001.geekbang.org/resource/image/5d/81/5d33b0b7dac595961c599e211f6eff81.jpg?wh=1920x1593)
我这次放的架构图里,所有我标识为红点的地方,都是这个系统里需要计算带宽的位置。也就是说,不管是针对外网出口还是只是在内网服务之间,都是需要计算一下带宽的。
现在有一些企业又做了子网划分这种情况下需要计算的点就更多了。好在计算公式不会有区别有区别的只是每个位置TPS和上下行数据的大小这些是要你通过容量场景统计得来的。
网络容量评估这一部分的内容就这么多,可以看出来,它的逻辑是比较清晰的。你只要找齐数据,代入公式计算就可以了。
## 总结
好了,这节课的内容就讲到这里。虽然刚才我很努力地没有讲一些数学理论(比如说:马尔可夫链),但是如果想要看懂这一讲的内容,还是需要一些数学基础的。
TPC-C的本意应该是希望能够在业内提供一套标准的基准测试逻辑通过它来指导系统性能容量规划。但是在具体的项目中因为有太多的局限所以我们只能拿它来做个参考。
相比较而言如果我们使用排队论来计算容量的话会更加精准。我们的原始数据只要来自于真实的系统并且通过检验方法比如说卡方检验、二项检验、K-S检验等得到了分布函数就可以根据排队论的逻辑进行系统的容量评估了。
请注意,这里我并没有代入前面我们提到的各种性能计数器。如果在实际的项目中,我们可以将性能计数器也作为容量规划模型的输入条件,模型将变得更加完整,数据也会更为准确。
而磁盘空间和网络容量的评估,我们直接根据实际的测试过程来做计算即可。
关于容量评估我就说到这里,希望能给你一些启发。
## 课后题
学完这节课,请你思考两个问题:
1. 你是如何来做系统级容量评估的?你有没有参照过具体的生产数据?
2. 你能不能用统计学中的模型比如差分自回归移动模型ARIMA来代替排队论呢
欢迎你在留言区与我交流讨论。我们下节课见!