Spark saveAsTable 位于 s3 存储桶的根本原因 NullPointerException



我使用的是Spark 3.0.1,我的分区表存储在s3中。请在这里找到问题的描述。

创建表

Create table root_table_test_spark_3_0_1 (
id string,
name string
)
USING PARQUET
PARTITIONED BY (id)
LOCATION  's3a://MY_BUCKET_NAME/'

导致第二次运行时出现NullPointerException的代码

Seq(MinimalObject("id_1", "name_1"), MinimalObject("id_2", "name_2"))
.toDS()
.write
.partitionBy("id")
.mode(SaveMode.Append)
.saveAsTable("root_table_test_spark_3_0_1")

当Hive元存储为空时,一切都正常,但当spark试图在InsertIntoHadoopFsRelationCommand阶段执行getCustomPartitionLocations时,问题就发生了。(例如第二次运行时(

实际上,它调用了以下方法:from(org.apache.hadoop.fs.Path(

/** Adds a suffix to the final name in the path.*/
public Path suffix(String suffix) {
return new Path(getParent(), getName()+suffix);
}

但是当我们处于根目录时,getParent()将返回null,从而导致NullPointerException。我目前唯一的选择是覆盖这个方法来做一些类似的事情:

/** Adds a suffix to the final name in the path.*/
public Path suffix(String suffix) {
return (isRoot()) ? new Path(uri.getScheme(), uri.getAuthority(), suffix) : new Path(getParent(), getName()+suffix);
}

当火花配置单元表的LOCATION处于根级别时,有人有问题吗?有什么变通办法吗?是否存在任何已知问题?

我的Runtime不允许我覆盖Path类和修复suffix方法,而且我不能从bucket的根目录中移动我的数据,因为它已经存在了两年。

这个问题的发生是因为我正在从Spark 2.1.0迁移到Spark 3.0.1,并且在Spark 2.2.0中出现了检查自定义分区的行为(https://github.com/apache/spark/pull/16460)

这整个上下文有助于理解问题,但基本上你可以通过轻松地再现它

val path: Path = new Path("s3a://MY_BUCKET_NAME/")
println(path.suffix("/id=id"))

FYI。hadoop的通用版本是2.7.4,请在这里找到完整的堆栈

NullPointerException
at org.apache.hadoop.fs.Path.<init>(Path.java:104)
at org.apache.hadoop.fs.Path.<init>(Path.java:93)
at org.apache.hadoop.fs.Path.suffix(Path.java:361)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.$anonfun$getCustomPartitionLocations$1(InsertIntoHadoopFsRelationCommand.scala:262)
at scala.collection.TraversableLike.$anonfun$flatMap$1(TraversableLike.scala:245)
at scala.collection.mutable.ResizableArray.foreach(ResizableArray.scala:62)
at scala.collection.mutable.ResizableArray.foreach$(ResizableArray.scala:55)
at scala.collection.mutable.ArrayBuffer.foreach(ArrayBuffer.scala:49)
at scala.collection.TraversableLike.flatMap(TraversableLike.scala:245)
at scala.collection.TraversableLike.flatMap$(TraversableLike.scala:242)
at scala.collection.AbstractTraversable.flatMap(Traversable.scala:108)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.getCustomPartitionLocations(InsertIntoHadoopFsRelationCommand.scala:260)
at org.apache.spark.sql.execution.datasources.InsertIntoHadoopFsRelationCommand.run(InsertIntoHadoopFsRelationCommand.scala:107)
at org.apache.spark.sql.execution.datasources.DataSource.writeAndRead(DataSource.scala:575)
at org.apache.spark.sql.execution.command.CreateDataSourceTableAsSelectCommand.saveDataIntoTable(createDataSourceTables.scala:218)
at org.apache.spark.sql.execution.command.CreateDataSourceTableAsSelectCommand.run(createDataSourceTables.scala:166)

感谢

看起来像是火花代码调用Path.suffix("something)的情况,因为根路径没有父路径,NPE被触发

长期修复

  1. 针对HADOOP在issues.apache.org上提交JIRA;提供一个带有fix-sufix((测试的补丁,以便在根路径上调用时正确降级。最适合所有人
  2. 不要将根路径用作表的目标
  3. 同时执行这两项操作

选项#2应该避免关于如何创建/提交表等方面的其他意外…有些代码可能会失败,因为尝试删除路径的根(此处为s3a://some bucket"(不会删除根,是吗?

换句话说:根目录有";奇数";语义无处不在;大多数时候,你在本地FS上没有注意到这一点,因为你从未尝试将/用作工作目的地,你会惊讶于rm-rf/与rm-rf/subdir等不同。Spark、Hive等从未被写入使用/作为工作目的地的程序,所以你会看到失败。

相关内容

  • 没有找到相关文章

最新更新