我有一个map-reduce过程,其中mapper从按键排序的文件中获取输入。例如:
1 ...
2 ...
2 ...
3 ...
3 ...
3 ...
4 ...
然后它被转换,99.9%的键相对于彼此保持相同的顺序,其余的99%接近。因此,以下可能是在上述数据上运行map任务的输出:
a ...
c ...
c ...
d ...
e ...
d ...
e ...
因此,如果您可以确保减速器接受一系列输入并将该减速器放在大多数输入已经定位的同一节点中,则重排将需要非常少的数据传输。例如,假设我对数据进行了分区,以便a-d由一个reducer处理,e-g由下一个处理。然后,如果a-d可以在处理1-4映射的同一节点上运行,则只需要通过网络发送e的两个记录。
如何构建一个利用我的数据属性的系统?我同时拥有Hadoop和Spark,并且不介意编写自定义分区器等。但是,完整的工作量是MapReduce的一个典型示例,我想坚持使用支持该范例的框架。
Hadoop mail archives提到这种优化的考虑因素。是否需要修改框架本身来实现它?
答案 0 :(得分:2)
从 SPARK 的角度来看,没有直接的支持:最接近 mapPartitions 与 preservePartions = true 。但是,这不会直接对您的情况有所帮助,因为密钥可能不会更改。
/**
* Return a new RDD by applying a function to each partition of this RDD.
*
* `preservesPartitioning` indicates whether the input function preserves the partitioner, which
* should be `false` unless this is a pair RDD and the input function doesn't modify the keys.
*/
def mapPartitions[U: ClassTag](
f: Iterator[T] => Iterator[U], preservesPartitioning: Boolean = false): RDD[U] = {
val func = (context: TaskContext, index: Int, iter: Iterator[T]) => f(iter)
new MapPartitionsRDD(this, sc.clean(func), preservesPartitioning)
}
如果您能够明确地知道没有任何密钥会移出其原始分区,则上述操作将起作用。但边界上的价值可能不合作。
与迁移密钥相比,数据的规模是多少?您可以考虑添加后处理步骤。首先为所有迁移密钥构建一个分区。您的映射器将为需要迁移的密钥输出特殊键值。然后对结果进行后处理,以便对标准分区进行某种附加。这是额外的麻烦,因此您需要在额外的步骤和管道复杂性中评估权衡。