我正在使用spark来计算用户评论的页面,但是当我在大数据集(40k条目)上运行代码时,我一直收到Spark java.lang.StackOverflowError
。当在少量条目上运行代码时,它工作正常。
条目示例:
product/productId: B00004CK40 review/userId: A39IIHQF18YGZA review/profileName: C. A. M. Salas review/helpfulness: 0/0 review/score: 4.0 review/time: 1175817600 review/summary: Reliable comedy review/text: Nice script, well acted comedy, and a young Nicolette Sheridan. Cusak is in top form.
守则:
public void calculatePageRank() {
sc.clearCallSite();
sc.clearJobGroup();
JavaRDD < String > rddFileData = sc.textFile(inputFileName).cache();
sc.setCheckpointDir("pagerankCheckpoint/");
JavaRDD < String > rddMovieData = rddFileData.map(new Function < String, String > () {
@Override
public String call(String arg0) throws Exception {
String[] data = arg0.split("\t");
String movieId = data[0].split(":")[1].trim();
String userId = data[1].split(":")[1].trim();
return movieId + "\t" + userId;
}
});
JavaPairRDD<String, Iterable<String>> rddPairReviewData = rddMovieData.mapToPair(new PairFunction < String, String, String > () {
@Override
public Tuple2 < String, String > call(String arg0) throws Exception {
String[] data = arg0.split("\t");
return new Tuple2 < String, String > (data[0], data[1]);
}
}).groupByKey().cache();
JavaRDD<Iterable<String>> cartUsers = rddPairReviewData.map(f -> f._2());
List<Iterable<String>> cartUsersList = cartUsers.collect();
JavaPairRDD<String,String> finalCartesian = null;
int iterCounter = 0;
for(Iterable<String> out : cartUsersList){
JavaRDD<String> currentUsersRDD = sc.parallelize(Lists.newArrayList(out));
if(finalCartesian==null){
finalCartesian = currentUsersRDD.cartesian(currentUsersRDD);
}
else{
finalCartesian = currentUsersRDD.cartesian(currentUsersRDD).union(finalCartesian);
if(iterCounter % 20 == 0) {
finalCartesian.checkpoint();
}
}
}
JavaRDD<Tuple2<String,String>> finalCartesianToTuple = finalCartesian.map(m -> new Tuple2<String,String>(m._1(),m._2()));
finalCartesianToTuple = finalCartesianToTuple.filter(x -> x._1().compareTo(x._2())!=0);
JavaPairRDD<String, String> userIdPairs = finalCartesianToTuple.mapToPair(m -> new Tuple2<String,String>(m._1(),m._2()));
JavaRDD<String> userIdPairsString = userIdPairs.map(new Function < Tuple2<String, String>, String > () {
//Tuple2<Tuple2<MovieId, userId>, Tuple2<movieId, userId>>
@Override
public String call (Tuple2<String, String> t) throws Exception {
return t._1 + " " + t._2;
}
});
try {
//calculate pagerank using this https://github.com/apache/spark/blob/master/examples/src/main/java/org/apache/spark/examples/JavaPageRank.java
JavaPageRank.calculatePageRank(userIdPairsString, 100);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
sc.close();
}
答案 0 :(得分:4)
当你的for循环变得非常大时,Spark无法再追踪血统。在for循环中启用检查点,以便每10次迭代检查一次rdd。检查点将解决问题。不要忘记清理检查点目录。
http://spark.apache.org/docs/latest/streaming-programming-guide.html#checkpointing
答案 1 :(得分:4)
我有多个建议可以帮助您大大提高问题中代码的性能。
一个例子是RDD。
count
- 告诉你行中的行数 文件,需要读取文件。所以如果你写RDD。count
,at 这一点文件将被读取,行将被计算,并且 计数将被退回。如果再次拨打RDD。
count
怎么办?同样的事情:文件将是 再读一遍。那么RDD。cache
做了什么?现在,如果你跑了 RDD。count
第一次,文件将被加载,缓存和 计数。如果您再次拨打RDD。count
,操作将使用 缓存。它只会从缓存中获取数据并计算 线条,没有重新计算。
详细了解缓存here。
在您的代码示例中,您不会重复使用已缓存的任何内容。因此,您可以从那里删除.cache
。
rddFileData
,rddMovieData
和rddPairReviewData
步骤,以便一次性完成。 摆脱.collect
,因为这会将结果带回驱动程序,也许是错误的实际原因。
答案 2 :(得分:3)
当您的DAG变大并且代码中发生过多级别的转换时,就会出现此问题。在最终执行操作时,JVM将无法保持执行延迟执行的操作。
检查点是一种选择。我建议为这种聚合实现spark-sql。如果您的数据是结构化的,请尝试将其加载到数据框中并执行分组和其他mysql函数来实现此目的。
答案 3 :(得分:0)
不幸的是,对我来说很容易解决的问题是,每经过几次迭代就调用.collect()
。嗯,至少可以快速解决问题。
急着,我无法提出使用检查点来工作的建议解决方案(也许无论如何也不会工作?)
注意:似乎也可以通过设置spark选项来解决问题……但是我现在没有时间,所以我没有检查如何从pyspark设置spark的java选项。更改配置的相关页面:
如果某人通过更改最大递归限制来使它起作用,那么这里的注释对其他人将是很好的。
答案 4 :(得分:0)
下面的事情修复了stackoverflow错误,正如其他人指出的那样,这是因为spark不断构建的血统,特别是当您在代码中有循环/迭代时。
设置检查点目录
spark.sparkContext.setCheckpointDir("./checkpoint")
您正在迭代中修改/操作的检查点数据帧/Rdd
modifyingDf.checkpoint()
缓存在每次迭代中重用的数据帧
reusedDf.cache()