我遇到了一个大规模的数据处理问题,我试图用Java中的Apache Spark解决这个问题。我的输入是一组相对较小的自定义Java对象。
我的地图步骤会对每个对象执行一些小的更改。完成后,它会识别对象所属的一个或多个等价类。结合起来,可能有数十亿个等价类/对象对。
我的问题是我需要对每个等价类的元素执行不同的操作。这个项目需要支持插件架构,所以我不知道等价类是什么,或者每个类需要发生的不同操作。
我的直觉是使用以下内容:
//Get the input set.
JavaRDD<MyType> input = ... //Not important
//Transform the input into (Equivalence Class, MyType) pairs,
//using strings to store the equivalence class.
JavaPairRDD<String, MyType> classedInput = input.flatMapToPair(
new PairFlatMapFunction<MyType, String, MyType>() {
Iterator<Tuple2<String, MyType>> call(MyType arg) {
List<Tuple2<String, MyType>> out = new ArrayList<>();
//Compute equivalence classes for arg.
for(String eqClz: getEquivalenceClasses(arg)) {
out.add(new Tuple2<String, MyType>(equClz, arg));
}
return out.iterator();
}
});
//Collapse the results for each equivalence class.
JavaPairRDD<String, MyType> output = classedInput.reduceByKey(
new Function2<MyType, MyType, MyType>() {
MyType call(MyType a, MyType b) {
String eqClz = ??? //<= Problem
List<MyModule> modules = MyFramework.getModulesForEqClz(eqClz);
for(MyModule m: modules) {
a = m.merge(a, b);
}
return a;
}
}
);
我希望能够将等价类传递给reduceByKey的函数,以便使用它来确定需要调用哪些模块。问题是我找不到Spark的键控合成器函数,而是将密钥传递给它们的回调函数。
由于classedInput的大小,我想避免使用MyType对象保存密钥,或者在地图后添加太多额外的分布式操作。
是否有一种类似Spark的方式来实现我尝试的目标?
答案 0 :(得分:0)
看来你的问题与"secondary sort"问题相反。我认为它可以通过逆解来解决(见下面的2)。
reduce
函数(或更完整的版本,aggregate
),只需要一个关联操作即可汇总数据结果,无论密钥如何。但是,在同一等价类中表达对项目分组的详细信息可能会有点复杂。
Tuple2<String, MyType> outValue = new Tuple2<String, MyType>(eqClz, arg);
out.add(new Tuple2<String, Tuple2<String, MyType>>(equClz, outValue));
如果您在评论中提到,当您传输数据时,您担心随机播放的大小,那么您想要限制的是用作键控结构的表示的大小。我的意思是,重复上面建议的值中的键会导致equClz变量的两个副本。但是如果是十几个字节,那么你要减小其大小的那个是关键位置的副本。为此,您可能希望选择正确长度的非加密哈希。
你提到每个记录多十几个字节会导致数十亿字节的数据,这意味着你最多有几亿条记录,因此最多只有几亿“equClz”值。这很容易被32位非加密哈希覆盖(你很容易找到这些哈希的实现,Murmur3,XXHash)。由于32位是4个字节,因此它应该将传输开销减少至少一个数量级。