原回答:
https://www.zhihu.com/question/279164955/answer/405448070
所谓的“性能”一般指的是吞吐和延迟这两件事。他们相互关联,但是互相的关系并不是简单的一一对应。
我改用一个通俗的例子来讲解这个问题。
假设你去一个乐高店去买散装乐高。首先你要排队。等你排到队首,跟店员说,你要小方块红色的10个,浅绿色花花的圆形15个,厚长条白色的3个……。店员就开始按照你说的给你去后边仓库拿。假设很少有两个顾客会有完全一样的要求,所以每次店员每次都拿的不一样。拿一趟的时间要数分钟。(假设店员比较傻,不会把仓库的东西拿一部分到柜台)
当你提完要求后,店员可以有两个做法:
第一种做法是:马上去给你拿。这时对于你来讲是最快的。店员去给你拿东西的时间是无法消除的,所以他马上给你拿,对于你来说等待时间是最小的。但是这仅仅是当你已经排在队首的情况。假如你在队伍中间,你前面每个人向店员买东西,店员都要去花几分钟拿一趟,实际上会让你等的更久。如果队伍很短,或者压根没有人排队,那么这样做还是比较合适的。但如果人比较多——进来排队的人比店员拿东西的速度更快,这样很快队伍就会排出大门,而店员,要不就累死,要不就得被顾客骂死。
第二种做法是:问你和你后边的几个人要什么,然后一趟拿回来。这时对于你来说就会需要多等一会。毕竟店员要挨个问,需要比问你一个人要多花一点点时间。但是店员给你拿一个,和给几个人同时拿多个乐高积木,跑腿的时间是几乎一样的。所以整体看来,付出了你需要多等一点点时间的代价,换来的是整个“吞吐”的提高。长时间看下来,店员可以接待完毕的顾客数量比上一种要大得多。并且,这里说的等待是说从你到了队首到拿到东西的时间。如果从你开始排队开始算等待时间,说不定等的时间还短点。
但是,店员一次行拿的东西总量毕竟还是有限的。如果比如他一次最多可以拿10人份的积木,而排队的人每次都能进来15个人排队。那么即便是店员次次满载工作,也没法满足顾客需要。对于顾客来讲,越排在后边的人等的时间越长。而单位时间内,店员能够接的顾客数量不会再增加,而是会保持稳定。(但我相信,此时,店员的心中一定是千万个草尼玛飞过的)
但这还没完。比如店员一次问10个人要的积木品种,然后去拿。但是10个人里有5个觉得已经等太久了,不想等了,就走人(超时了)。这时店员还是得把积木拿回来。但是顾客已经走了。于是店员的劳动就是无效的劳动。系统整体的利用率反而变得更低了。
相信通过这个例子你可以大概明白一个排队系统里吞吐和延迟的关系,大概就是:
- 当吞吐量小时,延迟比较低,但是延迟低到一定成都受限于网络延迟、磁盘IO延迟的物理限制,无法进一步降低;
- 通过batch/并发等方式,可以有效提高系统的吞吐,此时延迟会着上涨,但不会像吞吐量那样长的那么快。
- 当压力超过了系统的某个临界值,吞吐不升反降,并且延迟会急剧上升
然而,现实会更加的复杂。上面假设店员每次拿东西时的时间差不多,这已经是非常理想的情况了。如果根据要求,店员要跑3~5个不同的仓库,花的时间时多时少,那么系统的行为就会更加难以捉摸。极端情况下,店员憋不住了去大号(对,就是说你Java GC),这样一来一段时间内顾客的要求就完全得不到处理。
更复杂的情况是多层队列,即不光顾客要排队,店员去仓库拿东西也要排队,而仓库的管理员去总库拿东西还是要排队…… 这时候拿捏系统的性能表现就很难用简单的公式/模拟来搞定了。
所以工程上的做法都是要压测——就是要找到系统的临界值,将其作为设计的上限。生产环境中,绝对不要让系统接近临界值。
最后推荐一篇关于系统性能的文章:Thinking Clearly about Performance
其中的一副图很有说明性。
如果看完后觉得有一点点收获,不妨点个赞❤️~