使用 Hadoop 来分析数据
map 和 reduce
MapReduce 任务过程分为两个阶段:map 阶段和 reduce 阶段。每个阶段都以键值对作为输入和输出,其类型由程序员选择。
数据流
Hadoop 在存储有输入数据(HDFS 中的数据)的节点上运行 map 任务,可以获得最佳性能,这就是所谓的“数据本地化优化”,因为这样不需要使用集群的带宽资源。但是,有时候对于一个 map 任务的输入来说,存储有某个 HDFS 数据块备份的三个节点可能正在运行其他 map 任务,此时作业调度需要在三个备份中的某个备份(数据块)寻找同个机架中空闲的机器来运行该 map 任务。仅仅在非常偶然的情况下(基本不会发生),会使用其他机架中的机器运行该 map 任务,这将导致机架与机架之间的网络传输。
这也是为什么最佳分片的大小应该与块大小相同,因为它是确保可以存储在单个节点上的最大输入块的大小。如果分片跨越两个数据块,那么对于任何一个 HDFS 节点,基本上都不可能同时存储这两个数据块,因此分片中的部分数据需要通过网络传输到 map 任务节点。与使用本地数据运行整个 map 任务相比,显然是低效的。
map 的输出一般是写入本地磁盘而非 HDFS,因为 map 的输出是中间结果,该输出需要传递给 reduce 后才产生最终输出结果,而一旦作业完成,map 的输出结果就可以删除。因此,如果把它存储在 HDFS 上,难免有些小题大做。当然如果该节点上运行的 map 任务在传送中间结果给 reduce 时出错,Hadoop 会在另一个节点上重新运行这个 map 任务以再次构建 map 中间结果。
reduce 任务并不具备数据本地化的优势,因为 reduce 的输入通常总是来自于很多 mapper 的输出。
reduce 任务的数量并非有输入数据的大小决定,而事实上是独立指定的。如果有多个 reduce 任务,每个 map 任务就会针对输出进行分区(partition),也就是为每个 reduce 任务建一个分区。分区由用户定义的 partition 函数控制,但通常用默认的 partitioner 通过哈希函数来区分就已经很高效了。
combiner 函数
经过上面的介绍,我们很很容易地知道,尽量减少 map 和 reduce 之间的数据传输是有利的。Hadoop 允许我们针对 map 任务的输出指定一个 combiner 函数,该函数的输出作为 reduce 的输入。combiner 属于优化方案,不管调用 combiner 多少次,reduce 的输出结果应该都一样。
通俗地说就是能够提前合并的可以尽量通过 combiner 在 map 端就合并,比如求最值,求合这类计算,提前算和最后一起算结果都是一样的,但是注意:求平均值就不是了。
1 | max(0, 20, 10, 25, 15) = 25 |