尝试覆盖Hive分区时写入__HIVE_DEFAULT_PARTITION__的行损坏

时间:2018-10-02 16:28:18

标签: apache-spark hive apache-spark-sql

当我尝试使用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会导致我的数据混乱。将相同的数据集写入新表不会造成任何问题。

有人能对此有所启示吗?

2 个答案:

答案 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中说明:

https://spark.apache.org/docs/2.1.2/api/java/org/apache/spark/sql/DataFrameWriter.html#insertInto(java.lang.String)

  

与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|
    +---+---+