当我尝试使用Spark 2.3覆盖Hive表中的分区时,我看到一些非常奇怪的行为
首先,我在构建SparkSession时设置以下设置:
.config("spark.sql.sources.partitionOverwriteMode", "dynamic")
然后我将一些数据复制到新表中,并按date_id列进行分区。
ds
.write
.format("parquet")
.option("compression", "snappy")
.option("auto.purge", "true")
.mode(saveMode)
.partitionBy("date_id")
.saveAsTable("tbl_copy")
我可以在HDFS中看到已经创建了相关的date_id目录。
然后我创建一个数据集,其中包含要覆盖的分区的数据,该数据集包含单个date_id的数据,并按如下所示插入到Hive中:
ds
.write
.mode(SaveMode.Overwrite)
.insertInto("tbl_copy")
作为健全性检查,我将相同的数据集写入新表。
ds
.write
.format("parquet")
.option("compression", "snappy")
.option("auto.purge", "true")
.mode(SaveMode.Overwrite)
.saveAsTable("tmp_tbl")
tmp_tbl中的数据完全符合预期。
但是,当我查看tbl_copy时,会看到一个新的HDFS目录`date_id = HIVE_DEFAULT_PARTITION
查询tbl_cpy
SELECT * from tbl_copy WHERE date_id IS NULL
我看到应该插入分区date_id = 20180523的行,但是date_id列为空,并且不相关的row_changed列已填充值20180523。
看来,插入Hive会导致我的数据混乱。将相同的数据集写入新表不会造成任何问题。
有人能对此有所启示吗?
答案 0 :(得分:2)
因此看来分区列必须是数据集中的最后一个列。
我已经通过将以下方法应用于Dataset [T]来解决了这个问题。
def partitionsTail(partitionColumns: Seq[String]) = {
val columns = dataset.schema.collect{ case s if !partitionColumns.contains(s.name) => s.name} ++ partitionColumns
dataset.select(columns.head, columns.tail: _*).as[T]
}
答案 1 :(得分:0)
是的,这是一个棘手的行为,请在doc中说明:
与saveAsTable不同, insertInto会忽略列名称并仅使用 基于位置的分辨率。例如:
scala> Seq((1, 2)).toDF("i", "j").write.mode("overwrite").saveAsTable("t1")
scala> Seq((3, 4)).toDF("j", "i").write.insertInto("t1")
scala> Seq((5, 6)).toDF("a", "b").write.insertInto("t1")
scala> sql("select * from t1").show
+---+---+
| i| j|
+---+---+
| 5| 6|
| 3| 4|
| 1| 2|
+---+---+