这是《漫谈分布式系统》系列的第 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 大小等有关
算的快还不够,还要算的好。海量计算资源,如果管理不善,带来的就是巨大的浪费。下一篇,我们就一起看下,海量计算资源的管理和调度。
关联阅读
原创不易
关注/分享/赞赏
给我坚持的动力
点在看,给大家好看