我发生了一件奇怪的事:
import org.apache.avro.generic.{GenericData, GenericRecord}
import org.apache.avro.mapred.{AvroInputFormat, AvroWrapper, AvroKey}
import org.apache.avro.mapreduce.AvroKeyInputFormat
import org.apache.hadoop.io.{NullWritable, WritableUtils}
val path = "/path/to/data.avro"
val rdd = sc.newAPIHadoopFile(path, classOf[AvroKeyInputFormat[GenericRecord]], classOf[AvroKey[GenericRecord]], classOf[NullWritable])
rdd.take(10).foreach( x => println( x._1.datum() ))
在这种情况下,我得到了正确数量的记录返回,如果我查看rdd的内容,我会将各个记录视为tuple2的...但是,如果我在每个记录上打印如上所示,我得到了每次都有相同的结果。
显然这与Spark保持对其迭代的项目的引用有关,所以我需要在使用它之前克隆该对象。但是,如果我尝试克隆它,我会得到:
rdd.take(10).foreach( x => {
val clonedDatum = x._1.datum().clone()
println(clonedDatum.datum())
})
<console>:37: error: method clone in class Object cannot be accessed in org.apache.avro.generic.GenericRecord
Access to protected method clone not permitted because
prefix type org.apache.avro.generic.GenericRecord does not conform to
class $iwC where the access take place
val clonedDatum = x._1.datum().clone()
那么,我该如何克隆基准?
看起来我不是唯一遇到此问题的人:https://github.com/GoogleCloudPlatform/DataflowJavaSDK/issues/102。我无法弄清楚如何在我的情况下修复它,而不像链接公关中的那个人那样被黑客攻击。
建议?
答案 0 :(得分:2)
在邮件列表的帮助下,我想我有点想通了。来自SparkContext API docs:
&#39;&#39;&#39;注意:&#39;&#39;&#39;因为Hadoop的RecordReader类为每条记录重复使用相同的Writable对象,所以直接缓存返回的RDD或直接将其传递给聚合或shuffle操作将创建对同一对象的许多引用。如果您计划直接缓存,排序或聚合Hadoop可写对象,则应首先使用map函数复制它们。
所以,这就是我解决问题的方法:
val rdd = sc.newAPIHadoopFile(path,
classOf[AvroKeyInputFormat[GenericRecord]],
classOf[AvroKey[GenericRecord]],
classOf[NullWritable])
.map(_._1.datum) // <-- added this
rdd.take(10).foreach(println)