【MapReduce】超大集群的简单数据处理 part4

 

4 技巧

虽然简单写mapreduce函数实现基本功能就已经对大部分需要都足够了,我们还是开发了一些有用的扩展,这些在本节详细描述。

 

4.1 分区函数

 

MapReduce的使用者通过指定(R)来给出reduce 任务/输出文件的数量。他们处理的数据在这些任务上通过对中间结果key得分区函数来进行分区。缺省的分区函数时使用hash函数(例如hash(key)mod R)。这一般就可以得到分散均匀的分区。不过,在某些情况下,对key用其他的函数进行分区可能更有用。比如,某些情况下keyURL,那么我们希望所有对单个host的入口URL都保存在相同的输出文件。为了支持类似的情况,MapReduce函数库可以让用户提供一个特定的分区函数。比如使用hash(hostname(urlkey))mod R作为分区函数,这样可以让指向同一个hostnameURL分配到相同的输出文件中。

4.2 顺序保证

 

我们确保在给定的分区中,中间键值对key/value的处理顺序是根据key增量处理的。这样的顺序保证可以很容易生成每一个分区有序的输出文件,这对于输出文件格式需要支持客户端的对key的随机存取的时候就很有用,或者对输出数据集再作排序就很容易。

4.3 combiner函数

 

在某些情况下,允许中间结果key重复会占据相当的比重,并且用户定义的reduce函数满足结合律和交换律。比如2.1节的一个统计单词出现次数的例子。由于word的频率趋势符合Zipf 分布(齐夫分布),每一个map任务都回产生成百上千的<the,1>这样格式的记录。所有这些记录都通过网络发送给一个单个的reduce任务,通过reduce函数进行相加,最后产生单个数字。我们允许用户指定一个可选的组合函数Combiner函数,先在本地进行合并以下,然后再通过网络发送。

Combiner函数在每一个map任务的机器上执行。通常这个combiner函数的代码和reduce的代码实现上都是一样的。reduce函数和combiner函数唯一的不同就是MapReduce对于这两个函数的输出处理上不同。对于reduce函数的输出是直接写到最终的输出文件。对于combiner函数来说,输出是写到中间文件,并且会被发送到reduce任务中去。

部分使用combiner函数可以显著提高某些类型的MapReduce操作。附录A有这样的使用combiner的例子。

4.4 输入和输出类型

 

MapReduce函数库提供了读取几种不同格式的输入的支持。例如,”text”模式下,每行输入都被看成一个key/value对:key是在文件的偏移量,value是行的内容。另一个宠用格式保存了根据key进行排序key/value对的顺序。每一个输入类型的实现都知道如何把输入为了分别得map任务而进行有效分隔(比如,text模式下的分隔就是要确保分隔的边界只能按照行来进行分隔)。用户可以通过简单的提供reader接口来进行新的输入类型的支持。不过大部分用户都只用一小部分预先定义的输入类型。

reader函数不需要提供从文件读取数据。例如,我们很容易定义一个reader函数从数据库读取数据,或者从保存在内存中的数据结构中读取数据。

类似的,我们提供了一组用于输出的类型,可以产生不同格式的数据,并且用户也可以很简单的增加新的输出类型。

4.5 边界效应

 

在某些情况下,MapReduce的使用上,如果再map操作或者reduce操作时,增加辅助的输出文件,会比较有用。我们依靠程序来提供这样的边界原子操作。通常应用程序写一个临时文件并且用系统的原子操作:改名字操作,来再这个文件写完的时候,一次把这个文件改名改掉。

对于单个任务产生的多个输出文件来说,我们没有提供其上的两阶段提交的原子操作支持。因此,对于产生多个输出文件的,对于跨文件有一致性要求的任务,都必须是确定性的任务。这个限制到现在为止还没有真正在实际中遇到过。

4.6 跳过损坏的记录

 

某些情况下,用户程序的代码会让map或者reduce函数在处理某些记录的时候crash掉。这种情况下MapReduce操作就不能完成。一般的做法是改掉bug然后再执行,但是有时候这种先改掉bug的方式不太可行;也许是因为bug是在第三方的lib里边,它的原代码不存在等等。并且,很多时候,忽略一些记录不处理也是可以接受的,比如,在一个大数据集上进行统计分析的时候,就可以忽略有问题的少量记录。我们提供了一种执行模式,在这种执行模式下,MapReduce会检测到哪些记录会导致确定的crash,并且跳过这些记录不处理,使得整个处理能继续进行。

每一个worker处理进程都有一个signal handler,可以捕获内存段异常和总线错误。在执行用户map或者reduce操作之前,MapReduce函数库通过全局变量保存记录序号。如果用户代码产生了这个信号,signal handler于是用最后一口气通过UDP包向master发送上次处理的最后一条记录的序号。当master看到在这个特定记录上,有不止一个失效的时候,他就标志着条记录需要被跳过,,并且在下次重新执行相关的Map或者Reduce任务的时候跳过这条记录。

4.7 本地执行

 

因为实际执行操作时分布在系统中执行的,通常是在好几千台计算机上执行得,并且是由master机器进行动态调度的任务,所以对mapreduce函数的调试就比较麻烦。为了能够让调试方便,profiling和小规模测试,我们开发了一套MapReduce的本地实现,也就是说,MapReduce函数库在本地机器上顺序执行所有的MapReduce操作。用户可以控制执行,这样计算可以限制到特定的map任务上。用户可以通过设定特别的标志来执行他们的程序,同时也可以很容易的使用调试和测试工具(比如gdb)等等。

4.8 状态信息

 

master内部有一个HTTP服务器,并且可以输出状态报告。状态页提供了计算的进度报告,比如有多少任务已经完成,有多少任务正在处理,输入的字节数,中间数据的字节数,输出的字节数,处理百分比,等等。这些页面也包括了指向每个任务输出的标准错误和输出的标准文件的连接。用户可以根据这些数据来预测计算需要大约执行多长时间,是否需要为这个计算增加额外的计算资源。这些页面也可以用来分析为何计算执行的会比预期的慢。

此外,最上层的状态页面也显示了哪些worker失效了,以及他们失效的时候上面运行的mapreduce任务。这些信息对于调试用户代码中的bug很有帮助。

4.9 计数器

 

MapReduce函数库提供了用于统计不同事件发生次数的计数器。比如,用户可能想统计所有已经索引的German文档数量或者已经处理了多少单词的数量,等等。

为了使用这样的特性,用户代码创建一个叫做counter的对象,并且在mapreduce函数中在适当的时候增加counter的值。例如:

 

Counter* uppercase;

uppercase = GetCounter("uppercase");

 

map(String name, String contents):

       for each word w in contents:

              if (IsCapitalized(w)):

                     uppercase->Increment();

              EmitIntermediate(w, "1");

 

 

 

这些counter的值,会定时从各个单独的worker机器上传递给master(通过ping的应答包传递)。master把执行成功的map或者reduce任务的counter值进行累计,并且当MapReduce操作完成之后,返回给用户代码。当前counter值也会显示在master的状态页面,这样人可以看到计算现场的进度。当累计counter的值的时候,master会检查是否有对同一个map或者reduce任务的相同累计,避免累计重复。(backup任务或者机器失效导致的重新执行map任务或者reduce任务或导致这个counter重复执行,所以需要检查,避免master进行重复统计)。

部分计数器的值是由MapReduce函数库进行自动维持的,比如已经处理的输入的key/value对的数量,或者输出的key/value键值对等等。

counter特性对于MapReduce操作的完整性检查非常有用。比如,在某些MapReduce操作中,用户程序需要确保输出的键值对精确的等于处理的输入键值对,或者处理得German文档数量是在处理的整个文档数量中属于合理范围内。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值