之前有看到博文:《LoadRunner 没有告诉你的》之三——理发店模型,主要在讨论性能测试中的理发店模型,通过对理发店模型的理解来深入理解性能测试中对应的场景和性能指标的关系。当时对文中提到的两个观点不是很理解,或者不能很清晰的理解,观点如下:
保证最佳并发用户数要大于系统的平均负载。
确保系统的最大并发用户数要大于系统需要承受的峰值负载。
为什么要有这样保证?Why?今天带着对这两个观点的思考,再次重温了下理发店模型并通过详细分析文中的几个性能指标数据,已经比较清晰的理解文中提到的这两个观点了。以下是分析过程。
为了更贴近真实的性能测试中系统的行为,不妨对理发店模型描述如下:
1、理发店共有3名理发师;
2、每位理发师剪一个发的时间都是1小时;
3、理发店设有等待区,当顾客来到理发店发现没有理发师空闲而为他理发时,顾客就在等待区排队等待,每次理发师要为下一个顾客理发时就从等待区队列的最前面喊一个顾客为其理发,等待区可供排队的长度为无穷大;
4、顾客们都是很有时间观念的人而且非常挑剔,他们对于每次光顾理发店时所能容忍的等待时间+剪发时间是3小时,而且等待时间越长,顾客的满意度越低。如果3个小时还不能剪完头发,顾客就会极度的不满意而无法容忍(注意这里是一种极度不满意的状态,顾客虽然无法容忍但并不会立马生气的走人,你可以理解为他无可奈何,只能接受,可能下次就再也不会光顾这个理发店了,并且超过3小时的时间越长,不满意程度越深)
指标定义:
资源的利用情况(Utilization):理发店中正在忙于理发的理发师数量
吞吐量(Throughput):这里更准确的说是TPS,即单位时间(1小时)内完成理发的次数
响应时间(ResponseTime):这里包括顾客等待开始理发的时间与理发所花时间之和
设一次来理发店的顾客的数量为n,这里不考虑时间维度,那么可以得到如下顾客数量与三个指标的关系(最后一列响应时间数据分别对应每个用户的响应时间):
n | Utilization | Throughput | ResponseTime |
1 | 1 | 1 | 1 |
2 | 2 | 2 | 1 1 |
3 | 3 | 3 | 1 1 1 |
4 | 3 | 3 | 1 1 1 2 |
5 | 3 | 3 | 1 1 1 2 2 |
6 | 3 | 3 | 1 1 1 2 2 2 |
7 | 3 | 3 | 1 1 1 2 2 2 3 |
8 | 3 | 3 | 1 1 1 2 2 2 3 3 |
9 | 3 | 3 | 1 1 1 2 2 2 3 3 3 |
10 | 3 | 3 | 1 1 1 2 2 2 3 3 3 4 |
11 | 3 | 3 | 1 1 1 2 2 2 3 3 3 4 4 |
12 | 3 | 3 | 1 1 1 2 2 2 3 3 3 4 4 4 |
13 | 3 | 3 | 1 1 1 2 2 2 3 3 3 4 4 4 5 |
… | 3 | 3 | … |
对应到性能测试中,这里一次来理发店的顾客的数量可以理解为一次并发的用户数,或者是系统承受的负载(一次并发的用户数越多,系统承受的负载越大),因为是模型,所以我们不考虑当系统负载达到某个程度时,系统的资源利用率和吞吐量可能反而会下降。 观察数据可以看到,当一次并发的用户数n<=3时,响应时间始终稳定不变;而当一次并发的用户数3<n<=9时,响应时间(平均)有增加的趋势,但始终在可以忍受的程度之内(<=3);当并发用户数n>9时,响应时间继续增加,已经开始有部分用户的响应时间达到了极度不满意状态了;可以预测,如果继续将以上数据往下列的话,并发用户数n必然会在达到某个值时,所有的用户的响应时间都将达到极度不满意状态。
将以上分析对应到标准的软件性能模型(以上三个指标随负载的变化曲线)上,可以得出对于以上模型,最佳并发用户数为3,最大并发用户数为9。那么为什么需要保证最佳并发用户数要大于系统的平均负载呢?不是保证最大并发用户数大于系统的平均负载就可以了吗?
以上数据没有考虑时间维度,所以让我们产生了错觉(感觉只要系统的平均负载n不大于最大并发用户数9,响应时间就在可容忍度3内),如果我们增加时间维度,来观察时间抽每个时刻t(单位是1小时)上的并发用户数与三个指标的关系,就能看清答案了。我们还是用(多个)二维的表格来展示这个三维关系,再次重申这里的并发用户数可以理解为负载或请求系统处理的数量,并发用户数n=4意味着每单位时间内都让系统接受4个待处理的请求,L为等待区队列长度,也即是在该时刻没有被处理而等待处理的请求。(响应时间数据分别对应该时刻被系统处理的用户的响应时间)
n=1: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 1 | 1 | 1 | 0 |
2 | 1 | 1 | 1 | 0 |
3 | 1 | 1 | 1 | 0 |
4 | 1 | 1 | 1 | 0 |
5 | 1 | 1 | 1 | 0 |
6 | 1 | 1 | 1 | 0 |
7 | 1 | 1 | 1 | 0 |
8 | 1 | 1 | 1 | 0 |
9 | 1 | 1 | 1 | 0 |
10 | 1 | 1 | 1 | 0 |
… | 1 | 1 | 1 | 0 |
n=2: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 2 | 2 | 1 1 | 0 |
2 | 2 | 2 | 1 1 | 0 |
3 | 2 | 2 | 1 1 | 0 |
4 | 2 | 2 | 1 1 | 0 |
5 | 2 | 2 | 1 1 | 0 |
6 | 2 | 2 | 1 1 | 0 |
7 | 2 | 2 | 1 1 | 0 |
8 | 2 | 2 | 1 1 | 0 |
9 | 2 | 2 | 1 1 | 0 |
10 | 2 | 2 | 1 1 | 0 |
… | 2 | 2 | 1 1 | 0 |
n=3: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 0 |
7 | 3 | 3 | 1 1 1 | 0 |
8 | 3 | 3 | 1 1 1 | 0 |
9 | 3 | 3 | 1 1 1 | 0 |
10 | 3 | 3 | 1 1 1 | 0 |
… | 3 | 3 | 1 1 1 | 0 |
n=4: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 1 |
2 | 3 | 3 | 2 1 1 | 2 |
3 | 3 | 3 | 2 2 1 | 3 |
4 | 3 | 3 | 2 2 2 | 4 |
5 | 3 | 3 | 2 2 2 | 5 |
6 | 3 | 3 | 3 2 2 | 6 |
7 | 3 | 3 | 3 3 2 | 7 |
8 | 3 | 3 | 3 3 3 | 8 |
9 | 3 | 3 | 3 3 3 | 9 |
10 | 3 | 3 | 4 3 3 | 10 |
… | 3 | 3 | … | … |
n=5: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 2 |
2 | 3 | 3 | 2 2 1 | 4 |
3 | 3 | 3 | 2 2 2 | 6 |
4 | 3 | 3 | 3 2 2 | 8 |
5 | 3 | 3 | 3 3 3 | 10 |
6 | 3 | 3 | 3 3 3 | 12 |
7 | 3 | 3 | 4 4 3 | 14 |
… | 3 | 3 | … | … |
n=6: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 3 |
2 | 3 | 3 | 2 2 2 | 6 |
3 | 3 | 3 | 2 2 2 | 9 |
4 | 3 | 3 | 3 3 3 | 12 |
5 | 3 | 3 | 3 3 3 | 15 |
6 | 3 | 3 | 4 4 4 | 18 |
… | 3 | 3 | … | … |
n=7: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 4 |
2 | 3 | 3 | 2 2 2 | 8 |
3 | 3 | 3 | 3 2 2 | 12 |
4 | 3 | 3 | 3 3 3 | 16 |
5 | 3 | 3 | 4 4 3 | 20 |
… | 3 | 3 | … | … |
n=8: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 5 |
2 | 3 | 3 | 2 2 2 | 10 |
3 | 3 | 3 | 3 3 2 | 15 |
4 | 3 | 3 | 3 3 3 | 20 |
5 | 3 | 3 | 4 4 4 | 25 |
… | 3 | 3 | … | … |
n=9: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 6 |
2 | 3 | 3 | 2 2 2 | 12 |
3 | 3 | 3 | 3 3 3 | 18 |
4 | 3 | 3 | 3 3 3 | 24 |
5 | 3 | 3 | 4 4 4 | 30 |
通过以上数据不难发现,当系统负载n<=3时,随着时间不断向前推进,响应时间始终稳定如一,且排队等待处理的负载数始终为0;当系统负载n=4时,随着时间的推进,响应时间(平均)在逐渐增大,且前9小时响应时间没有超出容忍程度3,在第10小时已经开始有部分用户的响应时间超出了容忍程度处于极度不满意状态,可以预测第10小时之后,不断的会有更多的用户响应时间超出容忍程度,并最终所有用户响应时间都会超出容忍程度,另外还发现排队等待的负载数L也不断增大;当n>4是基本规律与n=4时一致,且n越大,(部分用户或所有用户)超出容忍程度的时刻越靠前,排队等待的负载数L也增长越快。
由此我们需要保证系统的最佳并发用户数要大于系统的平均负载,或者说系统的平均负载要小于最佳并发用户数,否则,必定会在某个时刻(该时刻到来的早还是迟,快还是慢,取决于系统承受负载的大小),响应时间会超出容忍程度,并最终至系统不可用。
那为什么又需要确保系统的最大并发用户数要大于系统需要承受的峰值负载呢?不是只要系统承受的负载大于最佳并发用户数,响应时间就必定在某个时刻超出容忍程度并最终变得不可用吗?另外以上统计数据还会给我们一个错觉就是,排队等待的请求数不能大于0,即不能存在有排队等待的现象,否则最终系统也还是会变得不可用的。
问题的关键就是上面这几个表格数据有一个隐含的条件--每个表格数据中系统承受的负载始终不变,比如n=4时,那么系统承受的负载始终为4(每小时都会承受4个待处理的请求)。而我们知道系统实际承受的负载不是稳定不变的,而是时刻可能会发生着变化的,变得比平均负载要大或小(上下波动),系统承受的峰值负载也就是系统承受的最大负载,这个承受最大负载持续的时间必定是某一段短暂的时间。因此我们改变上面的表格数据统计规则:因为最佳并发用户数为3,那么我们假设系统在第6小时这个时刻承受峰值负载(最大负载),在其他的时间承受的负载始终为最佳并发用户数负载,当然实际肯定是有某些时刻系统承受的负载要小于最佳并发用户数负载的,但这个我们并不关心。如下统计数据,nmax=4表示系统第6小时这个时刻承受的峰值负载为4。
nmax=4: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 1 |
7 | 3 | 3 | 2 1 1 | 1 |
8 | 3 | 3 | 2 1 1 | 1 |
9 | 3 | 3 | 2 1 1 | 1 |
10 | 3 | 3 | 2 1 1 | 1 |
… | 3 | 3 | … | … |
nmax=5: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 2 |
7 | 3 | 3 | 2 2 1 | 2 |
8 | 3 | 3 | 2 2 1 | 2 |
9 | 3 | 3 | 2 2 1 | 2 |
10 | 3 | 3 | 2 2 1 | 2 |
… | 3 | 3 | … | … |
nmax=6: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 3 |
7 | 3 | 3 | 2 2 2 | 3 |
8 | 3 | 3 | 2 2 2 | 3 |
9 | 3 | 3 | 2 2 2 | 3 |
10 | 3 | 3 | 2 2 2 | 3 |
… | 3 | 3 | … | … |
nmax=7: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 4 |
7 | 3 | 3 | 2 2 2 | 4 |
8 | 3 | 3 | 3 2 2 | 4 |
9 | 3 | 3 | 3 2 2 | 4 |
10 | 3 | 3 | 3 2 2 | 4 |
… | 3 | 3 | … | … |
nmax=8: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 5 |
7 | 3 | 3 | 2 2 2 | 5 |
8 | 3 | 3 | 3 3 2 | 5 |
9 | 3 | 3 | 3 3 2 | 5 |
10 | 3 | 3 | 3 3 2 | 5 |
… | 3 | 3 | … | … |
nmax=9: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 6 |
7 | 3 | 3 | 2 2 2 | 6 |
8 | 3 | 3 | 3 3 3 | 6 |
9 | 3 | 3 | 3 3 3 | 6 |
10 | 3 | 3 | 3 3 3 | 6 |
… | 3 | 3 | … | … |
nmax=10: | ||||
t | Utilization | Throughput | ResponseTime | L |
1 | 3 | 3 | 1 1 1 | 0 |
2 | 3 | 3 | 1 1 1 | 0 |
3 | 3 | 3 | 1 1 1 | 0 |
4 | 3 | 3 | 1 1 1 | 0 |
5 | 3 | 3 | 1 1 1 | 0 |
6 | 3 | 3 | 1 1 1 | 7 |
7 | 3 | 3 | 2 2 2 | 7 |
8 | 3 | 3 | 3 3 3 | 7 |
9 | 3 | 3 | 4 3 3 | 7 |
10 | 3 | 3 | 4 3 3 | 7 |
… | 3 | 3 | … | … |
通过以上表格数据,不难发现,只要系统承受的峰值负载nmax不大于最大并发用户数9,那么虽然已经有排队等待的现象发生,但是响应时间却始终都没有超出容忍程度3。而一但系统承受的峰值负载nmax大于最大并发用户数9(比如为10),那么必定会在某个时刻会有部分用户响应时间超出容忍程度。且系统承受的峰值负载超过最大并发用户数越多,响应时间超出容忍程度的用户数也越多,并最终至所有用户的响应时间超过容忍程度。即是要确保系统承受的峰值负载不大于系统的最大并发用户数,也就是要系统的最大并发用户数要大于系统需要承受的峰值负载。
以上所有分析都是基于理想模型和一定假设条件的,不能代表系统的真实性能状况,在真实的情况中需要考虑更多的因素,比如用于排队的等待区长度是有限的等等。但是以上分析可以为我们分析真实的性能数据提供一定参考、依据和思路,然后根据真实的性能数据与以上分析基准的差别来找到更多的因素。