分布式系统调优理论基础

174 阅读2分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

分布式系统性能问题十分复杂。系统调优犹如盲人摸象,因此需要统一的指导思想。本文从CPU角度出发,进行了分布式系统性能调优的模型建立。

当系统出现瓶颈时要能发现系统瓶颈所在。在压力测试下,一直压到性能上限。此时从
CPU角度进行调优分为两种情况

CPU利用率达到100%下的性能调优

1.减小计算量

​编辑
此时系统计算能力已经耗尽,那么说明系统结构上没有可调整的了,总有那么多计算要做,
调整系统结构无非改变了计算的顺序,不能改变计算总量,所以是无用功。
此时需要调优的是系统中最耗时的部分。此时要做的就是优化算法,目标是为了减小计算量。
如何找到系统最耗时的部分?这就需要完善的监控系统,统计各个模块的耗时。因为CPU已经
达到100%,所以耗时量就是计算量。 计算量=CPU Freq * time。如果CPU占用率不是100%,那么
不能用耗时来衡量一个模块的计算量。已经建立了TimeLineTree,一眼就能看出哪里耗时长。
需要注意的是不但要分析某一个模块执行一次的耗时,还要统计执行次数和总耗时。 因为耗时
短的模块可能因为循环执行次数过多而导致耗时过大。这类似于gprof这样的程序,不但要统计
每行代码的执行时间,还要统计执行次数。如果分布式程序不是简单的PipeLine,尤其要注意这点。

在调优前,先把系统分为任务集和资源集。 资源集是计算,存储,网络等等资源。

优化的目标:

  • 1.任务集固定的情况下:线性增加资源,性能也能线性增加。
  • 2.资源集固定的情况下:完成所有任务的耗时最小。 也可能有目标是每个任务平时等待时间最短。

达到目标1

对任务有一定的要求,即任务从开始到结束,各个资源(或者瓶颈资源)的利用率一直处于100%的状态。为达到此效果,job集必须能并行,以CPU资源为例,4核心时,job能并行利用4核心。 当扩展到8核心时,job也能利用8核心。这就要示Job代码实现上要能并行。最好将Job拆解的粒度小,能并行。
达到目标2

有两种方法:1.调整job的执行顺序,达到最优。 2.不断优化job,以减小job量(以CPU为例,就是减小计算量)
job的执行顺序影响着如cache命令中率,磁盘I/O顺序等等这此与顺序有的关的性能。
减小工作量:两个方向:算法的优化,如计算1到1亿的和,可以循环叠加,也可以直接用求和公式。显示求和公式优化了性能,减小了计算量。
另一个方向是消除重复计算,由于job过于复杂,可能有很多计算是重复的。可以拆解的更小,重叠子问题只要计算一次。

 具体调优手段参见:性能调优常规手段(理论) - 郭大侠1 - 博客园

2. 加入更多资源


如果算法已经无法优化,那么只能加入更多的CPU. 一直加到CPU利用率无法达到100%。无论
施加多少压力,都无法把CPU压到100%. CPU怎么加呢?
计算资源可全局调度的系统:
其实就是无脑加。可能有人会想,把CPU
加到最耗时的部分,其实不然,如果CPU能够在整个分布式系统中调度,那么无脑加直接生效。
想想,工作量是确定的,那么工作时间=工作量/CPU计算速度, 与先后计算哪部分无关。哪时需要
计算,CPU会被调度到哪里。哪里完成计算,会让出CPU。 最典型的例子就是linux操作系统这类分时
操作系统,CPU计算被分成时间片,公平使用。
计算资源不可全局调度的系统:
最好是各个模块同时增加计算资源。

加入更多的计算资源后会有两种情况:

  •     1.CPU利用率还是100%,那么不需要任何调整。
  •     2.CPU利用率达不到100%了,那么问题转化为CPU利用率没有达到100%下的性能调优。

     CPU利用率没有达到100%下的性能调优


大多数情况下,我们遇到的是这种情况。CPU利用率没有达到100%, 说明CPU在停下来等待。
发生这种现象有两种情况:

  • 1.系统中有一些非CPU任务,最常见的是I/O, 如网络请求,磁盘读取等。


这类问题的解决方法:使用更快的I/O设备,如SSD, 万兆网卡。总之想方设法提高I/O速度。

  • 2.系统中存在某个计算模块, 任务不可分解,无法100%利用CPU. 

此时其他任务已经运行完毕,让出了CPU给这个任务,但是这个任务却没法100%利用。
修改这个模块的算法,使之能够并行,利用所有的CPU.

总之,这种情况下我们可以把这个模块单独拿出来进行调优, 目标是在任意计算资源下(CPU数目下)
都能100%利用CPU. 调优完成后再放回到分布式系统中,依然可以很好的工作。

这里会遇到最棘手的问题,就是这个模块,无论怎么调整都没法完全利用CPU.此时可以考虑同时运行多个实例。

I/O操作和一些没法并发的计算量。


假设进行一次I/O需要cpu计算时间+I/O等待时间=(a, b)
进行一次计算需要纯CPU时间c。
那么想让CPU 100%需要并发数I/O数n
(na+nc) >= b.  n>=b/(a+c)
如启动时先发起n个I/O请求,b时间后I/O完成。
此时第二次再发起n个I/O,并完成第一次的n个数据的计算需要(na+nc)时间,
完成后,刚才第二次I/O的数据刚好完成。

I/O并发数=I/O等待时间/(cpu计算时间 + cpu 发起I/O时间)
n = b / (a + c)
如果 n = 1, 说明不需要调整
如果 n < 1, 说明I/O不是瓶颈,计算是瓶颈,这种情况下CPU利用率必然=100%.
如果 n > 1, 说明I/O是瓶颈, 需要加大I/O并发请求数。

CPU计算任务PipeLine中存在无法并发的计算量

​ 如job1: 无论多少CPU,至少使用a秒完成, 也就是说job1只能在x个cpu下实现100%CPU占用,而实际上有m个CPU.
这种情况只需要启动m/x个job1并发即可。 同时设置m/x大小的buffer,当job1执行完后放入buffer里,让后续任务
进行消费。 显然要求有多个Job1这种任务的存在。 一般情况下是存在的,比如网站请求,机器学习模型训练都
会有多个job1.  虽然多启动了几个job1,但是,整个系统的计算总量没有改变,也就是说并不会影响系统的整体吞吐上限。

同时另一个重要的地方是虽然这个耗时任务需要a秒,但是提高性能的方式只与当前不能被利用的处理器数有关,与其他任务的运行时间无关。所以不要看后续 任务只需要1秒,这个慢任务需要8秒,就想同时启动8个这个种慢任务。实际上于事无补。