Scala Spark:写入Avro文件
我在Spark,我有一个来自Avro文件的RDD。现在,我想对该RDD进行一些转换,并将其保存回Avro文件:Scala Spark:写入Avro文件,scala,serialization,avro,apache-spark,Scala,Serialization,Avro,Apache Spark,我在Spark,我有一个来自Avro文件的RDD。现在,我想对该RDD进行一些转换,并将其保存回Avro文件: val job = new Job(new Configuration()) AvroJob.setOutputKeySchema(job, getOutputSchema(inputSchema)) rdd.map(elem => (new SparkAvroKey(doTransformation(elem._1)), elem._2)) .saveAsNewAPIH
val job = new Job(new Configuration())
AvroJob.setOutputKeySchema(job, getOutputSchema(inputSchema))
rdd.map(elem => (new SparkAvroKey(doTransformation(elem._1)), elem._2))
.saveAsNewAPIHadoopFile(outputPath,
classOf[AvroKey[GenericRecord]],
classOf[org.apache.hadoop.io.NullWritable],
classOf[AvroKeyOutputFormat[GenericRecord]],
job.getConfiguration)
运行此Spark时,会抱怨架构$recordSchema不可序列化
如果我取消对.map调用的注释(并且只使用rdd.saveAsNewAPIHadoopFile),调用将成功
我做错了什么
有什么想法吗?Spark使用的默认序列化程序是Java序列化。因此,对于所有java类型,它将尝试使用java序列化进行序列化。AvroKey不可序列化,因此会出现错误 您可以在自定义序列化中使用KryoSerializer或插件(如Avro)。您可以在此处阅读有关序列化的更多信息
您还可以用可外化的东西包装对象。例如,在这里查看包装AvroFlumeEvent的SparkFlumeEvent:这里的问题与作业中使用的avro.Schema类的不可序列化性有关。当您尝试从map函数内的代码引用schema对象时,会引发异常 例如,如果您尝试执行以下操作,则会出现“任务不可序列化”异常:
val schema = new Schema.Parser().parse(new File(jsonSchema))
...
rdd.map(t => {
// reference to the schema object declared outside
val record = new GenericData.Record(schema)
})
您只需在功能块内创建一个新的模式实例,即可使一切正常工作:
val schema = new Schema.Parser().parse(new File(jsonSchema))
// The schema above should not be used in closures, it's for other purposes
...
rdd.map(t => {
// create a new Schema object
val innserSchema = new Schema.Parser().parse(new File(jsonSchema))
val record = new GenericData.Record(innserSchema)
...
})
由于您不希望为处理的每个记录解析avro模式,因此更好的解决方案是在分区级别解析模式。以下方法也有效:
val schema = new Schema.Parser().parse(new File(jsonSchema))
// The schema above should not be used in closures, it's for other purposes
...
rdd.mapPartitions(tuples => {
// create a new Schema object
val innserSchema = new Schema.Parser().parse(new File(jsonSchema))
tuples.map(t => {
val record = new GenericData.Record(innserSchema)
...
// this closure will be bundled together with the outer one
// (no serialization issues)
})
})
只要提供对jsonSchema文件的可移植引用,上述代码就可以工作,因为映射函数将由多个远程执行器执行。它可以是对HDFS中文件的引用,也可以与JAR中的应用程序一起打包(在后一种情况下,您将使用类装入器函数获取其内容)
对于那些试图将Avro与Spark一起使用的人,请注意,仍然存在一些未解决的编译问题,您必须在Maven POM上使用以下导入:
<dependency>
<groupId>org.apache.avro</groupId>
<artifactId>avro-mapred</artifactId>
<version>1.7.7</version>
<classifier>hadoop2</classifier>
<dependency>
org.apache.avro
.使用dataframe,使用databrics库创建avro非常简单
dataframe.write.format(“com.databricks.spark.avro”).avro($hdfs\u路径)
在您的例子中,输入是avro,所以它将有与之关联的模式,这样您就可以直接将avro读入dataframe,在转换之后,您可以使用上述代码将其写入avro
要将avro读入数据帧:
火花1.6
val dataframe=sqlContext.read.avro($hdfs_path)或val dataframe=
sqlContext.read.format(“com.databricks.spark.avro”).load($hdfs\u路径)
火花2.1
val dataframe=sparkSession.read.avro($hdfs_path)或val dataframe=
sparkSession.read.format(“com.databricks.spark.avro”).load($hdfs\u路径)
您能提供异常堆栈跟踪吗?Spark、Hadoop和Avro版本号也可能有用。请原谅我的天真。请问这里的工作是什么?看起来像是地图缩小作业?如果我们使用spark来写,为什么我们需要一个map reduce作业?当map函数中没有外部依赖项时,这个方法可以很好地工作。有没有办法使模式可序列化?