web-dev-qa-db-ja.com

Spark:Avroファイルへの書き込み

私は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))
   .saveAsNewAPIHadoopFile(outputPath, 
  classOf[AvroKey[GenericRecord]], 
  classOf[org.Apache.hadoop.io.NullWritable], 
  classOf[AvroKeyOutputFormat[GenericRecord]], 
  job.getConfiguration)

これを実行すると、Sparkは、Schema $ recordSchemaがシリアル化できないと文句を言います。

.map呼び出しのコメントを解除すると(そしてrdd.saveAsNewAPIHadoopFileがあるだけで)、呼び出しは成功します。

私はここで何が間違っているのですか?

何か案が?

17
user1013725

ここでの問題は、ジョブで使用されるavro.Schemaクラスの非直列化可能性に関連しています。 map関数内のコードからスキーマオブジェクトを参照しようとすると、例外がスローされます。

たとえば、次のように実行しようとすると、 "Task not serializable"例外が発生します。

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ファイルへの移植可能な参照を提供する限り機能します。これは、map関数が複数のリモートエグゼキューターによって実行されるためです。これは、HDFS内のファイルへの参照にすることも、JAR内のアプリケーションと一緒にパッケージ化することもできます(後者の場合、クラスローダー関数を使用してその内容を取得します)。

SparkでAvroを使用しようとしている場合は、まだ未解決のコンパイルの問題がいくつかあり、MavenPOMで次のインポートを使用する必要があることに注意してください。

<dependency>
  <groupId>org.Apache.avro</groupId>
  <artifactId>avro-mapred</artifactId>
  <version>1.7.7</version>
  <classifier>hadoop2</classifier>
<dependency>

"hadoop2"分類子に注意してください。問題は https://issues.Apache.org/jira/browse/SPARK-3039 で追跡できます。

5
Nicola Ferraro

Sparkで使用されるデフォルトのシリアライザーはJavaシリアル化です。したがって、すべてのJavaタイプについて、Javaシリアル化を使用してシリアル化を試みます。 AvroKeyはシリアル化できないため、エラーが発生します。

KryoSerializer、またはカスタムシリアル化(Avroなど)のプラグインを使用できます。シリアル化について詳しくは、こちらをご覧ください。 http://spark-project.org/docs/latest/tuning.html

外部化可能なものでオブジェクトをラップすることもできます。たとえば、AvroFlumeEventをラップするSparkFlumeEventをここで確認してください: https://github.com/Apache/spark/blob/master/external/flume/src/main/scala/org/Apache/spark/streaming/flume /FlumeInputDStream.scala

2
Gwen Shapira

データフレームを使用すると、databricsライブラリを使用してavroを作成するのは非常に簡単です。

dataframe.write.format( "com.databricks.spark.avro")。avro($ hdfs_path)

あなたの場合、入力はavroであるため、スキーマが関連付けられているため、avroをデータフレームに直接読み込むことができ、変換後、上記のコードを使用してavroに書き込むことができます。

Avroをデータフレームに読み込むには:

Spark 1.6

val dataframe = sqlContext.read.avro($ hdfs_path)OR val dataframe = sqlContext.read.format( "com.databricks.spark.avro")。load($ hdfs_path)

Spark 2.1

val dataframe = sparkSession.read.avro($ hdfs_path)OR val dataframe = sparkSession.read.format( "com.databricks.spark.avro")。load($ hdfs_path)

0
Sagar balai