Apache spark 如何将一个RDD拆分为两个或多个RDD?
我正在寻找一种将RDD拆分为两个或更多RDD的方法。我见过的最近的一个仍然是一个RDD 如果您熟悉SAS,请执行以下操作:Apache spark 如何将一个RDD拆分为两个或多个RDD?,apache-spark,pyspark,rdd,Apache Spark,Pyspark,Rdd,我正在寻找一种将RDD拆分为两个或更多RDD的方法。我见过的最近的一个仍然是一个RDD 如果您熟悉SAS,请执行以下操作: data work.split1, work.split2; set work.preSplit; if (condition1) output work.split1 else if (condition2) output work.split2 run; 这导致了两个不同的数据集。它必须立即持久化才能得到我想要
data work.split1, work.split2;
set work.preSplit;
if (condition1)
output work.split1
else if (condition2)
output work.split2
run;
这导致了两个不同的数据集。它必须立即持久化才能得到我想要的结果…不可能从一个转换中产生多个RDD*。如果要拆分RDD,必须为每个拆分条件应用
过滤器。例如:
def偶数(x):返回x%2==0
def奇数(x):返回非偶数(x)
rdd=sc.并行化(范围(20))
rdd_奇数,rdd_偶数=(rdd.filter(f)表示f in(奇数,偶数))
如果您只有一个二进制条件,并且计算成本很高,您可能会喜欢这样:
data work.split1, work.split2;
set work.preSplit;
if (condition1)
output work.split1
else if (condition2)
output work.split2
run;
kv_rdd=rdd.map(λx:(x,奇数(x)))
kv_rdd.cache()
rdd_奇数=千伏滤波器(λ千伏:千伏[1])。键()
rdd_偶数=千伏rdd.滤波器(λ千伏:非千伏[1])。键()
这意味着只有一个谓词计算,但需要对所有数据进行额外的传递
重要的是要注意,只要输入RDD被正确缓存,并且没有关于数据分布的额外假设,重复过滤和带有嵌套if-else的for循环之间的时间复杂度就没有显著差异
对于N个元素和M个条件,您必须执行的操作数显然与N乘以M成正比。对于for循环,它应该更接近(N+MN)/2,重复的过滤器正好是NM,但在一天结束时,它只不过是O(NM)。你可以看到我与**的讨论,了解一些利弊
在非常高的水平上,你应该考虑两件事:
Spark转换是懒惰的,除非您执行一个操作,否则您的RDD不会具体化
为什么这很重要?回到我的例子:
rdd_odd, rdd_even = (rdd.filter(f) for f in (odd, even))
如果后来我决定只需要rdd_奇数
,那么就没有理由实现rdd_偶数
如果查看SAS示例以计算work.split2
,则需要具体化输入数据和work.split1
RDD提供声明式API。当您使用过滤器
或映射
时,这完全取决于Spark engine如何执行此操作。只要传递给转换的函数没有副作用,它就创造了优化整个管道的多种可能性
归根结底,这种情况还不足以证明其自身的转变是正确的
这张带有滤镜图案的地图实际上用于核心火花。请参阅我对randomspilt
方法的回答和说明
如果唯一的目标是在输入时实现拆分,则可以为DataFrameWriter
使用partitionBy
子句来指定以下哪种文本输出格式:
def makePairs(row: T): (String, String) = ???
data
.map(makePairs).toDF("key", "value")
.write.partitionBy($"key").format("text").save(...)
*Spark中只有3种基本类型的转换:
- RDD[T]=>RDD[T]
- RDD[T]=>RDD[U]
- (RDD[T],RDD[U])=>RDD[W]
其中T,U,W可以是原子类型或/元组(K,V)。任何其他操作都必须使用上述的一些组合来表示。您可以查看更多详细信息
**
***另请参见如果使用拆分一个RDD,则返回一个RDD数组
如果希望返回5个RDD,请传入5个权重值
e、 g
一种方法是使用自定义分区器根据您的筛选条件对数据进行分区。这可以通过扩展Partitioner
并实现类似于RangePartitioner
的东西来实现
然后,可以使用映射分区从已分区的RDD构造多个RDD,而无需读取所有数据
val filtered=partitioned.mapPartitions{iter=>{
新迭代器[Int](){
覆盖def hasNext:Boolean={
if(rangeOfPartitionsToKeep.contains(TaskContext.get().partitionId)){
假的
}否则{
国际热核聚变实验堆
}
}
覆盖def next():Int=iter.next()
}
请注意,过滤后的RDD中的分区数将与已分区的RDD中的分区数相同,因此应使用合并来减少分区数并删除空分区。与上面提到的其他海报一样,没有单个、本机RDD转换来拆分RDD,但这里有一些“多路复用”可有效模拟RDD上各种“拆分”的操作,无需多次读取:
一些特定于随机拆分的方法:
方法可从开源silex项目获得:
解释其工作原理的博客帖子:
def muxPartitions[U:ClassTag](n:Int,f:(Int,迭代器[T])=>Seq[U],
持久化:存储级别):Seq[RDD[U]={
val mux=self.mapPartitionsWithIndex{case(id,itr)=>
单个迭代器(f(id,itr))
}.坚持(坚持)
Vector.tablate(n){j=>mux.mapPartitions{itr=>Iterator.single(itr.next()(j))}
}
def flatMuxPartitions[U:ClassTag](n:Int,f:(Int,迭代器[T])=>Seq[TraversableOnce[U]],
持久化:存储级别):Seq[RDD[U]={
val mux=self.mapPartitionsWithIndex{case(id,itr)=>
单个迭代器(f(id,itr))
}.坚持(坚持)
Vector.tablate(n){j=>mux.mapPartitions{itr=>itr.next()(j).toIterator}
}
正如其他地方提到的,这些方法确实需要在速度上权衡内存,因为它们通过“急切地”而不是“懒散地”计算整个分区结果来运行因此,这些方法可能会在大分区上遇到内存问题,而传统的惰性转换则不然。这难道不是@zero323的解决方案吗?他说它会多次读取它,这是我试图避免的。然而,每次调用mapPartitions时,它都会为每个分区运行任务分区内的实际数据(如果只读取onceok,但如果我将其保留为immed)