saveAsTable和insertInto在不同的SaveMode中有什么区别?

时间:2017-12-16 10:44:16

标签: scala apache-spark apache-spark-sql

我试图在DataFrame模式下HiveS3表(Overwrite}(我的申请需要)并需要在DataFrameWriter的两种方法(Spark / Scala)。根据我在documentation中可以阅读的内容,df.write.saveAsTable在以下方面与df.write.insertInto不同:

  • saveAsTable使用基于列名称的解析,而insertInto使用基于位置的解析
  • 在追加模式下,saveAsTable会更加关注现有表格的基础架构以制定某些决议

总体而言,它给我的印象是saveAsTable只是insertInto更智能的版本。或者,根据用例,可能更喜欢insertInto

但是这些方法中的每一种都有自己的一些注意事项,例如saveAsTable的性能损失(因为它包含更多功能)?除了在文档中讲述(不是很清楚)的内容之外,他们的行为还有其他差异吗?

修改-1

文档说明insertInto

  

将DataFrame的内容插入指定的表

saveAsTable

  

在表已存在的情况下,此函数的行为   取决于模式功能

指定的保存模式

现在我可以列出我的怀疑

  • insertInto总是希望表存在吗?
  • SaveMode会对insertInto产生影响吗?
  • 如果上述答案是肯定的,那么
    • 鉴于该表已存在,saveAsTableSaveMode.AppendinsertInto之间的区别是什么?
    • insertIntoSaveMode.Overwrite有什么关系吗?

4 个答案:

答案 0 :(得分:15)

免责声明我已经探索了insertInto一段时间了,虽然我离这个领域的专家很远,但我还是分享了更多的研究结果好。

  

insertInto总是希望表存在吗?

Yes(根据表名和数据库)。

此外,并非所有表都可以插入,即(永久)表,临时视图或临时全局视图都可以,但不是:

  1. 一张表格

  2. 基于RDD的表

  3.   

    SaveModes对insertInto有什么影响吗?

    (最近这也是我的问题!)

    是的,但只有SaveMode.Overwrite。在考虑insertInto之后,其他3种保存模式没有多大意义(因为它只是插入数据集)。

      

    如果表已经存在,saveAsTable与SaveMode.Append和insertInto之间的区别是什么?

    这是一个非常好的问题!我没有说,但是让我们只看一个例子(希望证明某事)。

    scala> spark.version
    res13: String = 2.4.0-SNAPSHOT
    
    sql("create table my_table (id long)")
    scala> spark.range(3).write.mode("append").saveAsTable("my_table")
    org.apache.spark.sql.AnalysisException: The format of the existing table default.my_table is `HiveFileFormat`. It doesn't match the specified format `ParquetFileFormat`.;
      at org.apache.spark.sql.execution.datasources.PreprocessTableCreation$$anonfun$apply$2.applyOrElse(rules.scala:117)
      at org.apache.spark.sql.execution.datasources.PreprocessTableCreation$$anonfun$apply$2.applyOrElse(rules.scala:76)
    ...
    scala> spark.range(3).write.insertInto("my_table")
    scala> spark.table("my_table").show
    +---+
    | id|
    +---+
    |  2|
    |  0|
    |  1|
    +---+
    
      

    insertInto和SaveMode.Overwrite有什么意义吗?

    我想是的,因为它非常关注SaveMode.Overwrite。它只是重新创建目标表。

    spark.range(3).write.mode("overwrite").insertInto("my_table")
    scala> spark.table("my_table").show
    +---+
    | id|
    +---+
    |  1|
    |  0|
    |  2|
    +---+
    
    Seq(100, 200, 300).toDF.write.mode("overwrite").insertInto("my_table")
    scala> spark.table("my_table").show
    +---+
    | id|
    +---+
    |200|
    |100|
    |300|
    +---+
    

答案 1 :(得分:6)

我想指出SPARK中SaveAsTableinsertInto之间的主要区别。

在分区表overwrite中,对于SaveAsTableinsertInto,SaveMode的工作方式有所不同。

请考虑以下示例。我正在使用SaveAsTable方法创建分区表。

hive> CREATE TABLE `db.companies_table`(`company` string) PARTITIONED BY ( `id` date);
OK
Time taken: 0.094 seconds
import org.apache.spark.sql._*
import spark.implicits._
import org.apache.spark.sql._

scala>val targetTable = "db.companies_table"

scala>val companiesDF = Seq(("2020-01-01", "Company1"), ("2020-01-02", "Company2")).toDF("id", "company")

scala>companiesDF.write.mode(SaveMode.Overwrite).partitionBy("id").saveAsTable(targetTable)

scala> spark.sql("select * from db.companies_table").show()
+--------+----------+
| company|        id|
+--------+----------+
|Company1|2020-01-01|
|Company2|2020-01-02|
+--------+----------+

现在我要添加2个新行和2个新分区值。

scala> val companiesDF = Seq(("2020-01-03", "Company1"), ("2020-01-04", "Company2")).toDF("id", "company")

scala> companiesDF.write.mode(SaveMode.Append).partitionBy("id").saveAsTable(targetTable)

scala>spark.sql("select * from db.companies_table").show()

+--------+----------+                                                           
| company|        id|
+--------+----------+
|Company1|2020-01-01|
|Company2|2020-01-02|
|Company1|2020-01-03|
|Company2|2020-01-04|
+--------+----------+

如您所见,表中添加了2个新行。

现在让我们说我想Overwrite对2020-01-02数据进行分区。

scala> val companiesDF = Seq(("2020-01-02", "Company5")).toDF("id", "company")

scala>companiesDF.write.mode(SaveMode.Overwrite).partitionBy("id").saveAsTable(targetTable)

根据我们的逻辑,只有分区2020-01-02应该被覆盖,但是SaveAsTable的情况有所不同。它将覆盖enter表,如下所示。

scala> spark.sql("select * from db.companies_table").show()
+-------+----------+
|company|        id|
+-------+----------+
|Company5|2020-01-02|
+-------+----------+

因此,如果我们只想使用SaveAsTable覆盖表中的某些分区,则不可能。

请参阅此链接以获取更多详细信息。 https://towardsdatascience.com/understanding-the-spark-insertinto-function-1870175c3ee9

答案 2 :(得分:0)

在将数据从spark 2.xx插入到现有的Hive动态分区表中时,我要考虑的另一个重要点:

df.write.mode("append").insertInto("dbName"."tableName")

以上命令将固有地映射“ df”中的数据,并且仅将新分区附加到现有表中。

希望,它在决定何时使用“ insertInto”时增加了另一点。

答案 3 :(得分:0)

我最近开始将Hive脚本转换为Spark,但我仍在学习。

我注意到saveAsTable和insertInto有一种重要的行为,尚未讨论。

df.write.mode(“ overwrite”)。saveAsTable(“ schema.table”)删除现有表“ schema.table”,并基于'df'模式重新创建新表。现有表的架构变得无关紧要,不必与df匹配。因为我的现有表是ORC,而创建的新表是镶木地板(Spark Default),所以我被这种行为所困扰。

df.write.mode(“ overwrite”)。insertInto(“ schema.table”)不会删除现有表,并且希望现有表的架构与'df'架构匹配。

我同时使用了两个选项检查了表的创建时间,并再次确认了行为。

原始表存储为ORC- 2019年9月4日星期三21:27:33 GMT

saveAsTable之后(存储更改为Parquet)-格林尼治标准时间2019年9月4日星期三21:56:23 (创建时间已更改)

删除并重新创建的原始表(ORC)- 2019年9月4日星期三21:57:38

insertInto之后(仍然是ORC)- 2019年9月4日星期三21:57:38 GMT (创建时间未更改)