Apache spark 如何分割一个巨大的rdd并轮流播放?
说明: 我们的spark版本是1.4.1 我们想要连接两个巨大的RDD,其中一个带有倾斜数据。因此,spark rdd操作连接可能会导致内存问题。我们试着把一个小的分成几块,然后分批播放。在每个广播回合中,我们尝试收集一部分较小的rdd到驱动程序,然后将其保存到HashMap,然后广播HashMap。每个执行器使用广播值对较大的rdd进行映射操作。我们通过这种方式实现了倾斜数据连接 但当它处理每一轮的广播值时。我们发现,我们不能破坏我们的广播价值后处理。如果我们使用broadcast.destroy(),下一步我们将处理数据 触发错误。像这样:Apache spark 如何分割一个巨大的rdd并轮流播放?,apache-spark,Apache Spark,说明: 我们的spark版本是1.4.1 我们想要连接两个巨大的RDD,其中一个带有倾斜数据。因此,spark rdd操作连接可能会导致内存问题。我们试着把一个小的分成几块,然后分批播放。在每个广播回合中,我们尝试收集一部分较小的rdd到驱动程序,然后将其保存到HashMap,然后广播HashMap。每个执行器使用广播值对较大的rdd进行映射操作。我们通过这种方式实现了倾斜数据连接 但当它处理每一轮的广播值时。我们发现,我们不能破坏我们的广播价值后处理。如果我们使用broadcast.destr
java.io.IOException: org.apache.spark.SparkException: Attempted to use Broadcast(6) after it was destroyed (destroy at xxx.java:369)
for (int i = 0; i < times; i++) {
JavaPairRDD<Tuple2<String, String>, Double> prevItemPairRdd = curItemPairRdd;
List<Tuple2<String, Double>> itemSplit = itemZippedRdd
.filter(new FilterByHashFunction(times, i))
.collect();
Map<String, Double> itemSplitMap = new HashMap<String, Double>();
for (Tuple2<String, Double> item : itemSplit) {
itemSplitMap.put(item._1(), item._2());
}
Broadcast<Map<String, Double>> itemSplitBroadcast = jsc
.broadcast(itemSplitMap);
curItemPairRdd = prevItemPairRdd
.mapToPair(new NormalizeScoreFunction(itemSplitBroadcast))
.persist(StorageLevel.DISK_ONLY());
curItemPairRdd.count();
itemSplitBroadcast.destroy(true);
itemSplit.clear();
}
我们查看了spark的源代码,发现这个问题是由rdd依赖关系导致的。如果rdd3->rdd2->rdd1(箭头显示相关性)。rdd1是使用名为b1的广播变量生成的,rdd2是使用b2生成的。在生成rdd3时,源代码显示需要序列化b1和b2。如果b1或b2在rdd3生产过程之前被破坏。它将导致我上面列出的故障
问题:
它是否存在这样一种方式,可以让rdd3忘记它的依赖关系,使它在生产过程中不需要b1、b2,只需要rdd2
或者它是否存在一种处理偏斜连接问题的方法
顺便说一句,我们已经为每个回合设置了检查点。并将spark.cleaner.ttl设置为600。问题仍然存在。若我们不销毁广播变量,执行器将在第五轮中丢失
我们的代码如下:
java.io.IOException: org.apache.spark.SparkException: Attempted to use Broadcast(6) after it was destroyed (destroy at xxx.java:369)
for (int i = 0; i < times; i++) {
JavaPairRDD<Tuple2<String, String>, Double> prevItemPairRdd = curItemPairRdd;
List<Tuple2<String, Double>> itemSplit = itemZippedRdd
.filter(new FilterByHashFunction(times, i))
.collect();
Map<String, Double> itemSplitMap = new HashMap<String, Double>();
for (Tuple2<String, Double> item : itemSplit) {
itemSplitMap.put(item._1(), item._2());
}
Broadcast<Map<String, Double>> itemSplitBroadcast = jsc
.broadcast(itemSplitMap);
curItemPairRdd = prevItemPairRdd
.mapToPair(new NormalizeScoreFunction(itemSplitBroadcast))
.persist(StorageLevel.DISK_ONLY());
curItemPairRdd.count();
itemSplitBroadcast.destroy(true);
itemSplit.clear();
}
for(int i=0;i
就个人而言,我会尝试一些不同的方法。让我们从一个小的模拟数据集开始
导入scala.util.Random
随机设定速度(1)
val left=sc.parallelize(
顺序填充(200)(“a”,随机填充(100))++
顺序填充(150)(“b”,随机填充(100))++
序列填充(100)(Random.nextPrintableChar.toString,Random.nexttint(100))
)
和按键计数:
val keysDistribution=left.countByKey
进一步假设第二个RDD均匀分布:
val right=sc.parallelize(
keysDistribution.keys.flatMap(x=>(1到5).map((x,)).toSeq)
并将每个键可处理的值数阈值设置为10:
val阈值=10
(k,v)
对,不如使用((k,i),v)
,其中i
是一个整数,它取决于给定k
的阈值和元素数量
val bucket=keysDistribution.map{
案例(k,v)=>(k->(v/阈值+1).toInt)
}
//将随机i分配给左边的每个元素
val leftWithSurrogates=left.map{case(k,v)=>{
val i=随机的.nextInt(桶(k))
((k,i),v)
}}
//从右到右复制每个值
val rightWithSurrogates=right.flatMap{case(k,v)=>{
图(i=>((k,i,v))
}}
val resultViaSurrogates=leftwithurrogates
.加入(右翼和右翼)
.map{case((k,u),v)=>(k,v)}
val unfrequentleft=left.filter{
情况(k,)=>键分布(k)<阈值
}
val unfrequenctright=right.filter{
情况(k,)=>键分布(k)<阈值
}
val unfrequent=unfrequentleft.join(unfrequentright)
接下来,让我们分别处理每个频繁密钥:
val frequentKeys=keysDistribution
.filter{case(v,v)=>v>=threshold}
.钥匙
val frequent=sc.union(frequentKeys.toSeq.map(k=>{
左.过滤器(u._1==k)
.cartesian(右.filter(u._1==k))
.map{case((k,v1),((u,v2))=>(k,(v1,v2))}
}))
最后,让两个子集合并:
val resultviaaunion=不频繁。联合(频繁)
val resultViaJoin=left.join(right).sortBy(identity.collect.toList
require(resultViaUnion.sortBy(identity.collect.toList==resultViaJoin)
require(resultViaSurrogates.sortBy(identity).collect.toList==resultViaJoin)
显然,这更多的是一个草图,而不是一个完整的解决方案,但应该给你一些如何继续的想法。与广播
it相比,它最大的优势在于消除了驱动程序瓶颈
它是否存在这样一种方式,可以让rdd3忘记它的依赖关系,使它在生产过程中不需要b1、b2,只需要rdd2
您使用了检查点和强制计算,但如果任何分区丢失,它仍然会失败。因此,您尝试连接两个RDD,但没有成功,因此您决定自己重新实现。我对这一决定表示怀疑。为什么您认为您的join实现会比Spark的更好?我宁愿建议你解决加入的问题。将这些问题发布为堆栈溢出问题。一个新手问题:如果
left
rdd中的数据高度倾斜(即同一个键的多个实例),那么rightWithSurrogates
rdd的大小将远大于原始right
rdd。那么我们是不是把备忘录上的问题复杂化了