如果我对groupByKey
执行基本的JavaRdd<Tuple2<String, String>>
操作,我会得到JavaPairRdd<Tuple2<String, Iterable<String>>>
:
someStartRdd.groupByKey()
因为每个元组中的迭代的大小将相当大(数百万)并且键的数量也将变得很大,我想以流式并行方式处理每个迭代,如同RDD。理想情况下,我喜欢每个键的RDD。
目前我唯一能想到的是收集,创建列表然后parallelize
:
List<Tuple2<String, Iterable<String>>> r1 = someStartRdd.groupByKey().collect();
for (Tuple2<String, Iterable<String>> tuple : r1){
List<String> listForKey = MagicLibrary.iterableToString(tuple._2());
JavaRdd<String> listRDD = sparkContext.parallelize(listForKey);
...start job on listRDD...
}
但我不想把所有东西都放在内存中来创建列表。更好的解决方案?
答案 0 :(得分:2)
如果你有大量的钥匙和每个钥匙的大量价值,你几乎没有运气。 Spark在长而窄的数据上工作得最好,将RDD分成多个RDD的唯一可靠方法是应用迭代过滤。你会在这里找到解释原因:How to split a RDD into two or more RDDs?
Scala Spark: Split collection into several RDD?中描述的另一种方法是明确地对数据进行分组,但由于它需要非惰性评估,因此不太可能使用大量密钥。
最后,由于2GB限制,数据偏差和大型shuffle的总体成本,重新分区可能不起作用。
记住所有这些可能的策略是尝试以一种利用方式构建算法,而不必明确地移动数据,除非有必要。您可以使用多种方法,包括采样,盐析和不同的近似值。
答案 1 :(得分:0)
你可以尝试以下解决方案,虽然我会建议不要使用它,因为它意味着很多shuffle操作,但是会实现你以流式并行方式处理每个键&#34; 迭代的目标,就像RDD一样。理想情况下,我喜欢每个密钥的RDD。&#34;
List<String> keys = someStartRdd.keys().distinct().collect();
HashMap<String,Integer> keysHash = new HashMap<String,Integer>();
int pos = 0;
for (String key : keys){
keysHash.put(key,pos++);
}
repartitionedRDD =
someStartRdd.repartitionAndSortWithinPartitions(
new CustomPartitioner(keysHash),//Partition your RDD
new CustomComparator()) //Sort by key the output
以CustomPartinioer
作为
public static class CustomPartitioner extends Partitioner implements Serializable
{
private static final long serialVersionUID = 1L;
private HashMap<String,Integer> keysHash;
public CustomPartitioner(HashMap<String,Integer> keysHash){
this.keysHash = keysHash
}
@Override
public int getPartition(Object key) {
return ((int) hashKeys.get((String) key);
}
@Override
public int numPartitions() {
return hashKeys.size();
}
}
之后,您可以流式并行方式处理&#34; &#34;像这样
repartitionedRDD.groupByKey().mapPartitions(new FlatMapFunction ...)