Java 处理具有非常倾斜数据的成对RDD时性能不佳
我有一个包含数百万个键值对的RDD对,其中每个值都是一个列表,可能包含单个元素或数十亿个元素。这会导致性能低下,因为大型组会将群集的节点阻塞数小时,而需要几秒钟的组无法并行处理,因为整个群集已经很忙 还有什么可以改进的吗 编辑: 给我带来问题的操作是一个Java 处理具有非常倾斜数据的成对RDD时性能不佳,java,scala,apache-spark,rdd,Java,Scala,Apache Spark,Rdd,我有一个包含数百万个键值对的RDD对,其中每个值都是一个列表,可能包含单个元素或数十亿个元素。这会导致性能低下,因为大型组会将群集的节点阻塞数小时,而需要几秒钟的组无法并行处理,因为整个群集已经很忙 还有什么可以改进的吗 编辑: 给我带来问题的操作是一个flatMap,其中分析了给定键的整个列表。键没有被触动,操作会将列表中的每个元素与列表的其他元素进行比较,这需要花费大量的时间,但不幸的是,必须这样做。这意味着整个列表需要同时位于同一节点中。生成的RDD将包含一个子列表,具体取决于在flatM
flatMap
,其中分析了给定键的整个列表。键没有被触动,操作会将列表中的每个元素与列表的其他元素进行比较,这需要花费大量的时间,但不幸的是,必须这样做。这意味着整个列表需要同时位于同一节点中。生成的RDD将包含一个子列表,具体取决于在flatMap
中计算的值
在这种情况下,我不能使用广播变量,因为不同的键值对之间不会使用公共数据。至于分区程序,根据O'Reilly Learning Spark的书,这种操作不会从分区程序中受益,因为不涉及洗牌(尽管我不确定这是不是真的)。在这种情况下,党派人士能帮忙吗
第二次编辑:
这是我的代码示例:
public class MyFunction implements FlatMapFunction
<Tuple2<String, Iterable<Bean>>, ComparedPerson> {
public Iterable<ProcessedBean> call(Tuple2<Key, Iterable<Bean>> input) throws Exception {
List<ProcessedBean> output = new ArrayList<ProcessedBean>();
List<Bean> listToProcess = CollectionsUtil.makeList(input._2());
// In some cases size == 2, in others size > 100.000
for (int i = 0; i < listToProcess.size() - 1; i++) {
for (int j = i + 1; j < listToProcess.size(); j++) {
ProcessedBean processed = processData(listToProcess.get(i), listToProcess.get(j));
if (processed != null) {
output.add(processed);
}
}
}
return output;
}
公共类MyFunction实现FlatMapFunction
{
公共Iterable调用(Tuple2输入)引发异常{
列表输出=新的ArrayList();
List listToProcess=CollectionsUtil.makeList(input._2());
//在某些情况下,尺寸=2,在其他情况下,尺寸>100.000
对于(int i=0;i
double for将循环
n(n-1)/2次,但这是不可避免的。这样的扭曲通常是特定于域的。您可以将您的值数据创建为RDD并加入其中。或者您可以尝试使用广播变量。或者您可以编写一个自定义分区程序,以帮助以不同方式分割数据
但是,最终,它将取决于数据的计算和具体情况。像这样的扭曲通常是特定于域的。您可以将您的值数据创建为RDD并在其上进行连接。或者您可以尝试使用广播变量。或者您可以编写一个自定义分区程序,以帮助以不同方式分割数据
但是,最终,它将取决于计算和数据的具体情况。处理键的顺序对总计算时间没有影响。唯一的问题是方差(一些值很小,其他值很大)我可以想象在处理结束时:一个大型任务仍在运行,而所有其他节点都已完成
如果您看到的是这样的情况,您可以尝试增加分区的数量。这将减少任务的大小,因此最终不太可能出现超大任务
广播变量和分区器对性能没有帮助。我认为你应该把重点放在尽可能高效地进行每件事到每件事的比较上。(或者更好的是,避免它。我认为二次算法在大数据中并不真正可持续。)处理键的顺序对总计算时间没有影响。方差(一些值小,其他值大)的唯一问题是在处理结束时:一个大任务仍在运行,而所有其他节点都已完成
如果您看到的是这样的情况,您可以尝试增加分区的数量。这将减少任务的大小,因此最终不太可能出现超大任务
广播变量和分区器对性能没有帮助。我认为你应该把重点放在尽可能高效地进行每件事到每件事的比较上。(或者更好的是,避免它。我认为二次算法在大数据中并不真正可持续。)如果“processData”很昂贵,那么您可以将该步骤并行化,并从中获得一些收益
在伪代码中,它类似于:
def processData(bean1:Bean, bean2:Bean):Option[ProcessedData] = { ... }
val rdd:RDD[(Key, List[Bean])] = ...
val pairs:RDD[(Bean, Bean)] = rdd.flatMap((key, beans) => {
val output = mutable.List[ProcessedBean]()
val len = beans.length
for (var i=0; i < len - 1; i++) {
for (var j=i+1; j < len; j++) {
output.add((beans(i), beans(j)))
}
}
output
}).repartition(someNumber)
val result:RDD[ProcessedBean] = pairs
.map(beans => processData(beans._1, beans._2))
.filter(_.isDefined)
.map(_.get)
def processData(bean1:Bean,bean2:Bean):选项[ProcessedData]={…}
val rdd:rdd[(键,列表[Bean])]=。。。
val对:RDD[(Bean,Bean)]=RDD.flatMap((key,Bean)=>{
val output=mutable.List[ProcessedBean]()
val len=beans.length
对于(变量i=0;iprocessData(bean.\u 1,bean.\u 2))
.filter(u.isDefined)
.map(u.get)
flatMap步骤仍将受最大列表的限制,并且在重新分区时会引起混乱,但是将processData步骤移到N^2步骤之外可能会获得一些并行性。如果“processData”很昂贵,则可以并行化该步骤并在那里获得一些收益
在伪代码中,它类似于:
def processData(bean1:Bean, bean2:Bean):Option[ProcessedData] = { ... }
val rdd:RDD[(Key, List[Bean])] = ...
val pairs:RDD[(Bean, Bean)] = rdd.flatMap((key, beans) => {
val output = mutable.List[ProcessedBean]()
val len = beans.length
for (var i=0; i < len - 1; i++) {
for (var j=i+1; j < len; j++) {
output.add((beans(i), beans(j)))
}
}
output
}).repartition(someNumber)
val result:RDD[ProcessedBean] = pairs
.map(beans => processData(beans._1, beans._2))
.filter(_.isDefined)
.map(_.get)
def processData(bean1:Bean,bean2:Bean):选项[ProcessedData]={…}
val rdd:rdd[(键,列表[Bean])]=。。。
val对:RDD[(Bean,Bean)]=RDD.flatMap((key,Bean)=>{
val output=mutable.List[ProcessedBean]()
val len=beans.length
对于(变量i=0;iprocessData(bean.\u 1,bean.\u 2))
.filter(u.isDefined)
.map(u.get)
flatMap步骤仍将以您的大