1

引言

随着计算技术的发展,有些应用需要非常巨大的计算能力才能完成,如果采用集中式计算,需要耗费相当长的时间来完成。怎么解决这个问题呢?当然是把这些问题分成多份,在不同的机器上去解决,众人拾柴火焰高嘛。而分布式计算就是将该应用分解成许多小的部分,分配给多台计算机进行处理。这样可以节约整体计算时间,大大提高计算效率。在分布式中,针对这种情况我们大概有两种计算模式:MapReduce和Stream,接下来就让我们来看看它们是何方神圣。注意:本文讲述的两种计算模式是以特定数据类型(分别对应静态数据和动态数据)作为计算维度。而在分布式领域中还有另外两种分布式计算模式,即 Actor 和流水线。它们是以计算过程或处理过程的维度的,不做本文讲述的重点。

Map Reduce

相信大家都有听说过Hadoop这个框架,这个框架主要用来解决海量数据的计算问题。那么它是如何做到海量数据的计算呢?你可能会想,既然是海量数据,有这么大的规模,那就让多个进程去处理,最后去汇总一下结果,这样就可以加大马力,提升速度了。
没错,就是这种想法,在分布式领域中我们称这种叫作MR(Map Reduce)模式。我们上边分成多个进程处理的想法可以归结成一个词--分而治之,是的,MR就是一个典型的分而治之(简称分治法)的代表。

分治法是什么

分治法就是将一个复杂的、难以直接解决的大问题,分割成一些规模较小的、可以比较简单的或直接求解的子问题,这些子问题之间相互独立且与原问题形式相同,递归地求解这些子问题,然后将子问题的解合并得到原问题的解。比如我们统计全国人口数量。

分治法的使用场景

  1. 问题规模比较大或复杂,且问题可以分解为几个规模较小的、简单的同类型问题进行求解;
  2. 子问题之间相互独立,不包含公共子问题;
  3. 子问题的解可以合并得到原问题的解。

分治法解决问题的步骤

  1. 分解原问题。将原问题分解为若干个规模较小,相互独立,且与原问题形式相同的子问题。
  2. 求解子问题。若子问题规模较小且容易被解决则直接求解,否则递归地求解各个子问题。
  3. 合并解,就是将各个子问题的解合并为原问题的解。

MR的抽象模型

了解了分治法之后,我们再来看看本段的主角MR,如下图所示,MapReduce 分为 Map 和 Reduce 两个核心阶段,其中 Map 对应“分”, 即把复杂的任务分解为若干个“简单的任务”执行;Reduce 对应着“合”,即对 Map 阶段的结果进行汇总。

mr1.png

在第一阶段,也就是 Map 阶段,将大数据计算任务拆分为多个子任务,拆分后的子任务通常具有如下特征: 相对于原始任务来说,划分后的子任务与原任务是同质的,比如原任务是统计全国人口数,拆分为统计省的人口数子任务时,都是统计人口数;并且,子任务的数据规模和计算规模会小很多。多个子任务之间没有依赖,可以独立运行、并行计算,比如按照省统计人口数,统计河北省的人口数和统计湖南省的人口数之间没有依赖关系,可以独立、并行的统计。
第二阶段,也就是 Reduce 阶段,第一阶段拆分的子任务计算完成后,汇总所有子任务的 计算结果,以得到最终结果。也就是,汇总各个省统计的人口数,得到全国的总人口数。

上边了解了这么多,那么在 MapReduce 里,各个组件是如何分工完成一个复杂任务的呢?

MR的工作原理

为了解答这个问题,我先带你了解一下 MapReduce 的组件结构。

mr2.png

如上图所示,MapReduce 主要包括以下三种组件:

  • Master,也就是 MRAppMaster,该模块像一个大总管一样,独掌大权,负责分配任 务,协调任务的运行,并为 Mapper 分配 map() 函数操作、为 Reducer 分配 reduce() 函数操作。
  • Mapper worker,负责 Map 函数功能,即负责执行子任务。
  • Reducer worker,负责 Reduce 函数功能,即负责汇总各个子任务的结果。

基于这三种组件,MapReduce 的工作流程如下所示:

mr3.png

程序从 User Program 开始进入 MapReduce 操作流程。其中图中的“step1,step2, ...,step6”表示操作步骤。

  • step1:User Program 将任务下发到 MRAppMaster 中。然后MRAppMaster 执行任 务拆分步骤,把 User Program 下发的任务划分成 M 个子任务(M 是用户自定义的数 值)。假设,MapReduce 函数将任务划分成了 5 个,其中 Map 作业有 3 个,Reduce 作 业有 2 个;集群内的 MRAppMaster 以及 Worker 节点都有任务的副本。
  • step2:MRAppMaster 分别为 Mapper 和 Reducer 分配相应的 Map 和 Reduce 作业。 Map 作业的数量就是划分后的子任务数量,也就是 3 个;Reduce 作业是 2 个。
  • step3:被分配了 Map 作业的 Worker,开始读取子任务的输入数据,并从输入数据中抽 取出 <key, value> 键值对,每一个键值对都作为参数传递给 map() 函数。
  • step4:map() 函数的输出结果存储在环形缓冲区 kvBuffer 中,这些 Map 结果会被定期 写入本地磁盘中,被存储在 R 个不同的磁盘区。这里的 R 表示 Reduce 作业的数量,也是 由用户定义的。在这个案例中,R=2。此外,每个 Map 结果的存储位置都会上报给 MRAppMaster。
  • step5:MRAppMaster 通知 Reducer 它负责的作业在哪一个分区,Reducer 远程读取相 应的 Map 结果,即中间键值对。当 Reducer 把它负责的所有中间键值对都读过来后,首 先根据键值对的 key 值对中间键值对进行排序,将相同 key 值的键值对聚集在一起,从而 有利于 Reducer 对 Map 结果进行统计。
  • step6:Reducer 遍历排序后的中间键值对,将具有相同 key 值的键值对合并,并将统计 结果作为输出文件存入负责的分区中。

从上述流程可以看出,整个 MapReduce 的工作流程主要可以概括为 5 个阶段,即: Input(输入)、Splitting(拆分)、Mapping(映射)、Reducing(化简)以及 Final Result(输出)。
所有 MapReduce 操作执行完毕后,MRAppMaster 将 R 个分区的输出文件结果返回给 User Program,用户可以根据实际需要进行操作。比如,通常并不需要合并这 R 个输出文 件,而是将其作为输入交给另一个 MapReduce 程序处理。

举个例子

我们来描述一个具体的例子来帮助大家理解,假设我们现在要统计苏锡常地区第二季度手机订单数量 Top3 的品牌。我们来看看具体的统计步骤吧。

  1. 任务拆分(Splitting 阶段)。根据地理位置,分别统计苏州、无锡、常州第二季度手机 订单 Top3 品牌,从而将大规模任务划分为 3 个子任务。
  2. 通过循环调用 map() 函数,统计每个品牌手机的订单数量。其中,key 为手机品牌, value 为手机购买数量(单位:万台)。如下图 Mapping 阶段所示(为简化描述,图中 直接列出了统计结果)。
  3. 与前面讲到的计算流程不同的是,Mapping 阶段和 Reducing 阶段中间多了一步 Shuffling 操作。Shuffling 阶段主要是读取 Mapping 阶段的结果,并将不同的结果划 分到不同的区。在大多数参考文档中,Mapping 和 Reducing 阶段的任务分别定义为映 射以及归约。但是,在映射之后,要对映射后的结果进行排序整合,然后才能执行归约 操作,因此往往将这一排序整合的操作单独放出来,称之为 Shuffling 阶段。
  4. Reducing 阶段,归并同一个品牌的购买次数。
  5. 得到苏锡常地区第二季度 Top3 品牌手机的购买记录。

mr4.png

由上述流程可以看出,Map/Reduce 作业和 map()/reduce() 函数是有区别的:

  • Map 阶段由一定数量的 Map 作业组成,这些 Map 作业是并发任务,可以同时运行, 且操作重复。Map 阶段的功能主要由 map() 函数实现。每个 Map 作业处理一个子任务 (比如一个城市的手机消费统计),需要调用多次 map() 函数来处理(因为城市内不同 的居民倾向于不同的手机)。
  • Reduce 阶段执行的是汇总任务结果,遍历 Map 阶段的结果从而返回一个综合结果。与 Reduce 阶段相关的是 reduce() 函数,它的输入是一个键(key)和与之对应的一组数 据(values),其功能是将具有相同 key 值的数据进行合并。Reduce 作业处理一个分 区的中间键值对,期间要对每个不同的 key 值调用一次 reduce() 函数。在完成 Map 作 业后,每个分区中会存在多个临时文件;而执行完 Reduce 操作后,一个分区最终只有 一个输出文件。

根据上文我们知道MR模式的核心思想是分治法,在这种模式下任务完成后整个进程就结束了,而且它并不适合去处理实时任务。实时性任务主要是针对流数据的处理,对处理时延要求很高,通常需要有常驻服务进程,等待数据的随时到来随时处理,以保证低时延。处理流数据任务的计算模式,在分布式领域中叫作 Stream。

流式计算

流式计算是什么

近年来,由于网络监控、传感监测、AR/VR 等实时性应用的兴起,一类需要处理流数据的 业务发展了起来。比如各种直播平台中,我们需要处理直播产生的音视频数据流等。这种如流水般持续涌现,且需要实时处理的数据,我们称之为流数据。它有什么特征呢?1、数据如流水般持续、快速地到达;2、海量数据规模,数据量可达到 TB 级甚至 PB 级;3、对实时性要求高,随着时间流逝,数据的价值会大幅降低; 4、数据顺序无法保证,也就是说系统无法控制将要处理的数据元素的顺序。那么,在分布式领域中,对于这种流数据的计算模式就是流计算,也叫做Stream。因为流数据大量、快速、时变的特点,所以它通常被用于处理数据密集型应用。

流式计算的工作原理

因为流式计算强调的是实时性,数据一旦产生就会被立即处理,所以在当一条数据处理完成后会序列化存储到缓存中,然后立刻通过网络传输到下一个节点,由下一个节点继续处理,而不是像MapReduce 那样,等到缓存写满才开始处理、传输。为了保证数据的实时性,在流计算中,不会存储任何数据,就像水流一样滚滚向前。那么,它的处理流程是怎么样的呢?使用流计算进行数据处理一般会有三个步骤,参见下图:

str1.png

  • step1:提交流式计算作业。怎么理解呢?一个模式运行的前提你需要有一定的制度,不然流计算系统它不知道怎么去处理数据不就搞笑了么。所以,这一步去做的是给计算系统灌输一个“制度”,其中包括处理节点的个数,数据转发的规则等。另外,流式计算作业是常驻计算服务。
  • step2: 加载流式数据进行计算。流式计算在启动后就一直处于待触发态,等到一旦有数据过来就立即执行计算逻辑去处理。从上图中我们可以看出,在流计算系统中,有多个流处理节点,流处理节点会对数据进行预定义的处理操作,并在处理完后按照某种规则转发给后续节点继续处理。此外,流计算系统中还存在管理节点,主要负责管理处理节点以及数据的流动规则。
  • step3:实时计算结果。流式计算作业在得到小批量数据的计算结果后,可以立刻将结 果数据写入在线 / 批量系统,无需等待整体数据的计算结果,以进一步做到实时计算结果的实时展现。

小结一下,流计算是处理持续到达的数据,它并不会去存储数据,适用于对数据处理有较高实时性要求的场景,比如网络监控,传感检测,AR/VR和视频流等实时应用。

以上我们分别学习了MapReduce(批处理)和Stream(流式计算)模式,我们也对它们有了一些了解,虽然这两种计算模式对数据的处理方式不同,但都是以特定数据类型(分别对应静态数据和动态数据)作为计算维度。

下期预告

【分布式系统遨游】分布式数据存储

关注我们

欢迎对本系列文章感兴趣的读者订阅我们的公众号,关注博主下次不迷路~

Nosay


NoSay
449 声望544 粉丝