30

漫谈分布式系统(五):再也不怕算得慢

 4 years ago
source link: https://mp.weixin.qq.com/s/cTTfgqXMbVjBPaoaj0hJUw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

这是《漫谈分布式系统》系列的第 5 篇,预计会写 30 篇左右。每篇文末有为懒人准备的 TL;DR,还有给勤奋者的关联阅读。扫描文末二维码,关注公众号,听我娓娓道来。也欢迎转发朋友圈分享给更多人。

系列第一篇中,我们提到,为了解决算得慢的问题,必须让计算并行起来。

本质上还是分治法,先把计算拆分成一个个小任务执行,再合并这些任务的结果得到最终结果。

虽然理论上没有关联,但在实践上,计算的分治,是以存储的分治为前提的。否则,每个任务都要处理全量的数据,这个性能和资源开销是接受不了的。所以前面两篇我们讲了分布式存储作为前置知识。

今天这篇,我们就一起看看一个最简单的分布式计算系统是个什么样子。

1

计算的分治,其实可以从两个角度理解:

  • 计算逻辑的分治

  • 计算结果的分治

计算逻辑的分治,说白了,就是代码的并行执行。

并且很显然,为了保证结果的正确性,多机/多进程并行运行的代码是完全一样的。

区别只是在于处理的数据不同。这也是为什么前面说计算的分治是以存储的分治为前提。

所以,其实计算逻辑的分治,只是同样代码的不同实例,在相互独立地并行执行而已。

前面我们讲到分布式存储引擎的时候,探讨过怎么切分数据的问题。那分布式计算框架呢,又该怎么切分计算逻辑?

刚才已经说了,计算逻辑的分治区别只在处理的数据不同,所以计算逻辑的切分也就等价于数据的切分。

当然,这里对数据的切分,目的已经不同于我们在分布式存储里,为了更均衡的存储海量数据而切分到 block 这个粒度了。

我们的目的只是为了提高计算的并行度,再考虑到计算逻辑处在应用层,所以通常以文件为基本单位做切分。

比如,如果文件太小,可以几个文件合在一起处理;如果文件太大,又可以一个文件拆成几份并行处理(见上篇文章提到的文件的压缩和切分)。

2

而计算结果的分治就不一样了。

我们处理数据的目的是为了得到结果。为了提高性能,采用了分布式计算的方法,使得同样的代码被并行运行起来,每个代码实例都处理一部分的数据。

但是最终结果只能有一个,所以必须把每个实例处理的结果合并起来。

这也就导致原本单机单线程顺序执行的逻辑,被迫切分成了两部分。

看一个简单的例子。

如果我们有一个 100 万亿的整数数组,想要对每个元素乘 2 之后求和。方便起见,以 Python 为例,并且只展示 9 个数字的情况:

a = [1, 2, 3, 4, 5, 6, 7, 8, 9]

数据量小的时候,我们可以单机单线程处理,直接一个循环搞定:

result = 0

for i in a:

result += i*2

但是在分布式的场景下,就不得不拆成两步去做了(只是示意,下面的代码仍然是单机运行)。

第一步,做乘 2 的处理:

b = map(lambda x: x*2, a)

// output: [2, 4, 6, 8, 10, 12, 14, 16, 18]

第二步,汇总结果:

reduce(lambda x,y: x+y, b)

// output: 90

不难看出,map 和 reduce 这两个函数,顾名思义,就很好的描述了自己的职责。

map 和 reduce 是典型的函数式编程的概念。主流分布式框架 Hadoop 的组成部分 -- 分布式计算框架 MapReduce 就得名于此。本质上就是先 map 再 reduce 这个分治思想的多机版本实现。

3

那到底 MapReduce 是以什么粒度来拆分任务的呢?

map 和 reduce 有不同的处理方式。

对于 map,上面已经讲了,计算逻辑的分治取决于输入数据在文件层面的分治。

具体来说,输入一共有多少个 split,就会自动启动多少个 map 任务。

而 split 数,由文件格式和 block 大小决定。比如,如果一个文件可以切分(splittable),那这个文件就会被拆分成 file_size/block_size 这么多份;如果一个文件没法被拆分,哪怕大小是 block_size 的 100 倍,也只能被一个 map 任务处理(所以要尽量避免)。

而 reduce 就不一样了。

map 阶段的输出就是 reduce 阶段的输入。map 阶段的输入我们往往不能控制,但 map 的输出是完全在我们掌控之下的。所以 reduce 的输入就不像 map 的输入那样受限了,自然,reduce 阶段的并行度 -- 也就是 reducer 的数量,就是可以设置的了。

recucer 的数量,通常就是最终结果文件的数量。reducer 数量设置为 0 时,就会省略 reduce 阶段,跑完 map 阶段,程序就结束了。

reducer 数量具体设置成多少,就是运行速度和资源消耗的权衡了,可以根据实际情况灵活调整。

TL;DR

最简单的分布式计算系统,和分布式存储引擎一样,本质上仍然是分治法的运用:

  • 为了节省资源,计算的分治,是以存储的分治为前提的

  • 计算的分治,可以从两个角度理解:计算逻辑的分治和计算结果的分治

  • 计算逻辑的分治,只是相同代码的多机并行执行,区别只是处理的数据不同

  • 计算的分布式导致了计算过程被切分为 map 和 reduce 两个阶段

  • 计算结果的分治,是计算过程被切割后自然的影响

  • map 阶段的并行度取决于输入数据的切分度,和文件格式、压缩方式、block 大小等有关

  • reduce 阶段的并行度可以自行设置,是运行速度和资源消耗的权衡

算的快还不够,还要算的好。海量计算资源,如果管理不善,带来的就是巨大的浪费。下一篇,我们就一起看下,海量计算资源的管理和调度。

原创不易

关注/分享/赞赏

给我坚持的动力

yEfMje3.jpg!web

点在看,给大家好看


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK