MapReduce:在大型集群上简化数据处理(4)

原文链接:https://mp.weixin.qq.com/s/jA4FeYBjb6fd_JyP6fzQ5g

5 性能

在本节中,我们将通过在大型计算机集群上运行的两个计算来测试MapReduce的性能。其中一个计算任务是在1TB的数据中查找某个特定的东西。另一个计算任务则是对1TB左右大小的数据进行排序。

这两个程序能代表了MapReduce的用户编写的程序的很大一部分用途。其中一类是将数据从一种表示变为另一种表示。另一类则是从一个大型数据集中提取出小部分人们所感兴趣的数据。

5.1 集群配置

所有的程序都会在一个包含了大约1800台机器的集群中执行。每台机器都有两颗允许超线程技术的2GHz的Intel至强处理器,4GB内存,两块160GB大小的IDE硬盘,以及1Gbit速度的以太网络连接。这些机器被安排在两层树形交换网络中,根节点处可提供大约100-200Gbps的总带宽。所有机器都存放在同一托管机房中,因此,任意一对机器间的往返时间都小于1毫秒。

在4GB的内存中,大约有1-1.5GB左右的空间被运行在该集群中的其他任务所占用。这些程序会在周末下午执行,因为那时的CPU,磁盘以及网络大部分都处于空闲状态。

5.2 Grep

grep 程序可以从10^10条大小为100byte的数据中找到一条相对少见的数据(该数据在这个数据集中出现了92337次)。该输入数据被拆分为大约64MB大小的片段(总共有15000个这样的片段),并且整个输出结果会放在一个文件中。
在这里插入图片描述
Figure 2中所展示的是在一段时间内的计算进展。Y轴所表示的是扫描输入数据的速率。随着分配给用于MapReduce计算的机器数量的增加,速率也随之逐渐提高。并且当分配了1764台机器时,峰值速率超过了30GB/s。当map任务结束后,速率开始下掉,在大约80秒处,速率变为0。整个计算过程从开始到结束花了大约150秒左右。这其中包含了一分钟左右的启动开销。这种开销是由于需要将程序分发到所有测试机器上,以及与GFS进行交互以打开1000个输入文件的集合,并获取进行位置优化所需的信息而导致的延迟。

5.3 Sort

sort程序是用来对10^10条大小为100byte(大约为1TB大小的数据)的记录进行排序处理。该程序是以TeraSort基准为模型。

该排序程序所包含的代码行数少于50行。一个三行的Map函数能从一行文本中提取10byte大小的排序键,并将该键和该原始文本作为中间键值对。我们使用一个内置的Identity函数当作Reduce操作符来执行。该函数对传入的中间键值对不做改变,直接作为输出键值对输出。最终排序后的输出结果会被写入一组双向备份的GFS文件中(例如,要向文件中写入2TB大小的输出数据)。

和之前一样,输入数据会被拆分为15000个大小为64MB左右的数据块。我们将排序后的输出数据放入4000个文件中。分区函数会使用key中的初始字节,将它分为4000份。

我们针对该基准的分区函数是建立在key分配的相关知识之上。在一个普通的排序程序中,我们会在里面添加一个预传递的MapReduce操作,该操作用来收集样本键,并使用所采样的样本键的分布情况来计算出最终排序过程中的分割点。
在这里插入图片描述
Figure 3(a)中向我们展示了该排序程序的一次正常执行过程。左上方的图表示的是输入文件读取的速率。这张图上的峰值速率大约为13GB/s,由于所有的Map任务都在200秒前完成,所以它的速度很快就降了下来,变成了0。注意,此处读取输入文件的速率要小于grep中的速率。这是因为排序Map任务花费了大约一半的时间和I/O带宽将中间输出写入它们的本地硬盘中。grep所对应的中间数据的大小可以忽略不计。

中间左边的图像展示的是map任务所产生的数据通过网络传送给reduce任务时的速率。一旦第一个map任务完成后,shuffle就会开始。图中第一个驼峰处代表的是第一批大概1700个Reduce任务(整个MapReduce计算会被分配给1700机器,每台机器最多一次只会执行一个reduce任务)。大概执行计算300秒后,第一批reduce任务中的部分任务会完成,接着,我们对剩下的reduce任务进行shuffle。shuffle总共花了600秒完成。

左下方的图像表示的是Reduce任务将排序后的数据写入到最终的输入文件时的速度。在第一次shuffle阶段结束后和写入阶段开始前,它们之间存在了延迟。因为机器会忙于对中间数据进行排序。硬盘的写入速度会处于2-4GB/s,并且这个速度会持续一段时间。所有的写入需要花费850秒才能完成。包括启动开销,整个计算过程花费了891秒。这个速度和TeraSort benchmark中的当前最佳纪录差不多。

还有一些事情值得注意。输入速度要比shuffle速度和输出速度高得多,这是要归功于我们的地区性优化。大部分数据都是从本地硬盘中读取,这样就避开了通过受限的网络进行数据传输了。shuffle速度要比输出速度高不少。原因是因为输出阶段要输出排序后数据的两份备份(为了保证可靠性和可用性,我们要有两份输出备份)。我们要写入两个备份的原因是,我们的底层文件系统提供了可靠性和可用性的机制。如果底层文件系统使用类似容错编码的方式而不是复制的方式来保证数据的可靠性和可用性,那么在输出数据写入硬盘的时候,就可以降低网络带宽的使用。

5.4 高效的Backup任务

在Figure 3 (b)中,我们展示了一个禁用备用任务的排序任务的执行过程。该执行过程和Figure 3(a)中所示的类似,除了最后这段时间的尾巴很长,几乎没有任何写入活动发生。在960秒后,除了最后5个reduce任务以外,其他任务全部完成了。然而,这些最后几个拖后腿的任务花了300秒才完成。整个计算过程花了1283秒,多了44%的执行时间。

5.5 机器故障

在Figure 3©中演示的排序程序里,我们在程序开始后的几分钟内故意砍掉了1746个worker中的200个worker。底层集群调度器会立即在这些机器上重启新的worker进程(这些机器依然能够正常运行,只是worker进行被关闭)。

worker进程的关闭会在图中显示一个负的输入速度,这是因为之前一些已经完成的map任务丢失所导致的,这些丢失的map任务需要被再次执行。重新执行这种Map任务的速度会相对较快。整个计算任务包括启动开销在内,花了933秒完成(只比正常执行时间多用了5%的时间)。

6 经验

我们在2003年2月完成了MapReduce库的第一个版本,并且在2003年8月份我们对它进行了显著的优化,这其中包括了地区性优化,worker机器之间任务执行的动态负载均衡等等。从那时起我们发现,MapReduce库能广泛地应用于我们所遇见的各种问题。谷歌已经将其应用于非常多的领域之上:

大规模机器学习问题

谷歌新闻以及Froogle产品的集群问题

提取用于生成流行查询报告的数据(例如,谷歌的Zeitgeis)

从新实验和产品的网页中提取相关属性(例如从本地化搜索的语料库中提取位置信息)

以及大规模图计算问题
在这里插入图片描述
在这里插入图片描述
Figure 4 显示了在我们的主要源代码管理系统中,随着时间的推移,MapReduce程序在数量上显著的增长。从2003年一开始的0个MapReduce程序,到2004年9月已经有了差不多900个不同的MapReduce程序实例。MapReduce是如此的成果,因为它使得编写一个简单的程序并在半小时内在一千台计算机上高效运行它成为可能,从而极大地加快了开发和原型开发周期。此外,它允许没有分布式或者并行系统经验的程序员轻松地去利用这些资源。

在每项job完成后,MapReduce库会以日志的方式记录下该job所利用的计算资源的数据。在Table 1中,我们展示了谷歌在2004年8月所运行的MapReduce job中某个子集的运行数据。

6.1 大规模索引

到目前为止,MapReduce最成功的应用那就是重写了谷歌网络搜索服务所使用到的用来生成数据结构的索引系统。索引系统会将我们抓取系统所检索到的大量文档作为输入,并保存到一组GFS文件中。这些文档的原始内容的数据大小超过20TB。在索引过程中,通常会运行5-10次MapReduce操作。使用MapReduce已经为我们提供了以下便利(与之前版本中所使用的分布式索引系统相比):

索引相关的代码会更加简单,小巧并且易于理解。因为那些用于处理容错,分布式以及并行的代码已经隐藏在MapReduce库中,所以无需去担心这些。例如,使用MapReduce库,处理计算相关的C++代码从原来的3800行减少到了大概700行代码。

MapReduce库的性能已经足够好了,这样我们可以将概念上无关的计算分离,而不是将它们全混在一起,来避免对数据的额外传递。这使得我们容易去改变索引处理过程。例如,原本在我们旧的索引系统中要去做一处修改需要花数月才能完成,但在新系统中只需花几天就能实现。

索引处理过程执行起来变得更加容易,因为大部分由机器故障,机器缓慢,网络瞬间阻塞所引发的问题已经自动被MapReduce库所解决了,这也不需要操作人员的介入了。此外,通过在索引集群中增加新的机器,这可以很容易的提升索引过程的性能。

7 相关工作

许多系统都提供了受限制的编程模型,通过使用这些限制来让计算自动并行化执行。例如,一个函数可以把它的所有前缀放入一个长度为N的数组内,使用并行前缀计算,将它分布在N个处理器上,在log N时间内完成计算。MapReduce可以看做是根据我们在现实世界中大型计算的相关经验所得到的模型,对这些模型进行简化和精炼的成果。更重要的是,我们提供了可扩展到数千个处理器的容错实现。相比之下,大部分并行处理系统的实现只适用于小规模场景使用,并且它们将处理机器故障的细节交由程序员处理。

Bulk同步编程以及一些MPI(消息传递接口)原语提供了更高级的抽象,这使得程序员能更容易得写出并行程序。这些系统和MapReduce之间的主要区别在于MapReduce利用受限制的编程模型来自动并行化用户程序,并且还提供了透明的容错能力。

我们的备用任务机制和Charlotte系统中提供的eager调度机制相似。Eager调度机制的一个缺点就是,如果一个任务反复失败,那么整个计算任务就无法完成。在我们的机制中,通过跳过有问题的记录,以此在某种程度上修复这种问题。

MapReduce的实现依赖于内部的集群管理系统。该系统负责在一个超大的共享机器的集群中分发和运行用户任务。虽然这并不是本文的重点,但是本质上来讲,该系统和Condor等其他系统类似。

排序机制是MapReduce库中的一部分,它和NOW-Sort中的操作类似。源机器(也就是map worker)将要排序的数据进行分区,然后将这些输入发送给R个reduce worker中的一个去处理。每个reduce worker会在本地将数据进行排序。当然,NOW-Sort并没有用户所定义的Map和Reduce函数,这也使得我们的库运用范围更加广泛。

River提供了一种编程模型,在该模型中,进程通过在分布式队列上发送数据来相互通信。和MapReduce类似,即使存在硬件故障或者系统问题,River系统也能提供良好的平均性能。River通过对磁盘和网络通讯进行仔细的调度,以此来平衡任务的完成时间。MapReduce则采用了不同的方法。通过限制编程模型,MapReduce框架能够将一个问题拆分成很多粒度良好的任务。这些任务会动态调度给可用的worker,这样执行速度快的worker就能处理更多的工作(能者多劳)。这种受限制的编程模型也允许我们在任务快要结束时安排备用任务,这大大减少了在配置不均衡的情况下(例如,又卡又慢的机器拖累整个进度)的任务完成时间。

BAD-FS则有一个和MapReduce非常不同的编程模型。与MapReduce不同,它是针对面向广域网的任务执行。但是,它们有两个基本相似点。(1)它们都使用冗余执行来恢复因为数据丢失而导致的任务失败。(2)它们都是用了数据本地化调度策略,减少通过拥堵的网络发生的数据量。

TACC是一个用于简化构建高可用网络服务的系统。和MapReduce一样,它也依靠重新执行机制来实现容错处理。

8 总结

谷歌已经将MapReduce编程模型成功应用于许多不同的场景。我们将此成功归因于许多原因。首先,该模型易于使用,即便是没有并行和分布式系统经验的程序员也可以轻松使用。因为它隐藏了并行,容错,局部优化,负载均衡这些细节。其次,很大一部分问题都能以MapReduce计算进行表达。例如,在谷歌的许多产品中(例如,网页搜索服务、排序、数据挖掘、机器学习以及其他系统),MapReduce被用于生成数据。第三点则是我们已经开发了一种MapReduce的实现。该实现可以扩展应用到一个包含数千台机器的集群之上。该实现可以有效使用这些机器资源。因此,它适用于谷歌所遇到的许多大型计算问题。

从这项工作中我们学到了以下几点。首先,通过限制编程模型可以很容易进行并行和分布式计算,这样可以使这种计算具备容错性。其次,网络带宽是一种稀缺资源。因此,在我们的系统中存在着许多优化。主要针对于减少通过网络发送的数据量:其中,位置优化可以让我们能够从本地磁盘中读取数据,并且在本地磁盘中写入一份中间数据的副本,以此来节省网络带宽。第三,可以使用冗余执行来减少速度比较慢的机器所带来的影响,并且可以处理计算机故障和数据丢失。

字数频率统计

本节包含一个完整程序,该程序对在命令行上指定的一组输入文件中每个唯一单词的出现次数进行计数

#include "mapreduce/mapreduce.h"
// User’s map function
class WordCounter : public Mapper {
   public: virtual void Map(const MapInput& input) {
       const string& text = input.value(); const int n = text.size(); for (int i = 0; i < n; ) {
           // Skip past leading whitespace
           while ((i < n) && isspace(text[i]))
              i++;
           // Find word end
           int start = i;
           while ((i < n) && !isspace(text[i]))
              i++;
           if (start < i)
              Emit(text.substr(start,i-start),"1");
      }
  }
};  


REGISTER_MAPPER(WordCounter);
// User’s reduce function
class Adder : public Reducer {
   virtual void Reduce(ReduceInput* input) {
       // Iterate over all entries with the
       // same key and add the values
       int64 value = 0;
       while (!input->done()) {
           value += StringToInt(input->value());
           input->NextValue();
      }
       // Emit sum for input->key()
       Emit(IntToString(value));
  }
};

REGISTER_REDUCER(Adder);
int main(int argc, char** argv) {
   ParseCommandLineFlags(argc, argv);
   MapReduceSpecification spec;
   // Store list of input files into "spec"
   for (int i = 1; i < argc; i++) {
       MapReduceInput* input = spec.add_input();
       input->set_format("text");
       input->set_filepattern(argv[i]);
       input->set_mapper_class("WordCounter");
  }
   // Specify the output files:
   // /gfs/test/freq-00000-of-00100
   // /gfs/test/freq-00001-of-00100
   // ...
   MapReduceOutput* out = spec.output();
   out->set_filebase("/gfs/test/freq");
   out->set_num_tasks(100);
   out->set_format("text");
   out->set_reducer_class("Adder");
   // Optional: do partial sums within map
   // tasks to save network bandwidth
   out->set_combiner_class("Adder");


   // Tuning parameters: use at most 2000
   // machines and 100 MB of memory per task

   spec.set_machines(2000);
   spec.set_map_megabytes(100);
   spec.set_reduce_megabytes(100);

   // Now run it
   MapReduceResult result;
   if (!MapReduce(spec, &result)) abort();

   // Done: ’result’ structure contains info
   // about counters, time taken, number of
   // machines used, etc.
   return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值