问题描述
最近收到一个任务报错:
Saving data in the Hive serde table
bigdata
.books
is not supported yet. Please use the insertInto() API as an alternative..;
简单描述下业务逻辑:
- 判断要操作的表是否存在,如果存在drop掉该表:
spark.sql("use bigdata")
spark.sql("DROP TABLE IF EXISTS books")
- 创建表:
spark.sql("create table books(a int,b string)")
- 构造dataframe:
val df = spark.sql("...业务数据...")
- 数据写入表:
df.write.mode(SaveMode.Append).saveAsTable("books")
问题分析
第2步其实是创建了一个hive表,创建的这个表books在hive层面是兼容的,即通过spark写入到books的数据通过hiveSQL是可以读的,那还有hive不能读的情况吗?答案是:有!
Spark内部其实是有两套API的:DataSource API和 基于Hive实现的API。在创建表的时候,如果不显示使用例如using parquet
指定source的话,默认是创建hive兼容表的,就像上面第2步代码一样。如果指定了source,比如:
spark.sql("create table using parquet books(a int,b string)")
那么books表中的数据,使用hiveSQL就不一定能读到。
DataSource API创建的表的meta信息写入了Hive metaStore,但是hiveSQL并不能辨认,导致数据是读不出来的。
回到上面的报错。因为第2步创建了一张hive兼容表,但是saveAsTable其实是DataSource API,即使用DataSource API像Hive兼容表里保存数据,Spark现在的版本(spark-2.1.0)还是不支持的。
怎么知道saveAsTable是DataSourceAPI 呢,就需要跟一下代码了:
找到DataFrame写入口DataFrameWrite
的saveAsTable
方法,跟到最后:
找到执行的逻辑计划,进入
CreateTable
看一下:
它对应的是datasource 级别的ddl,这样就很清楚了。
关于DataSource 的源码分析,后面会专门写一篇文章分析一下。
问题解决
那怎么解决上面的问题呢?
思路就是保持API的一致。就上面具体问题来说,有两种方案:
- 错误里面提示了,使用insertInto("table")替代:insertInto 就是Hive兼容的API,用它写入的数据通过hiveSQL是能够读出来的。
- 不事先创建
table
表,直接通过saveAsTable创建,创建出的表跟df有同样的schema。但是注意,这样创建的表就是DataSource表了,通过hiveSQL可能是读不了的。
所以如果需要hiveSQL能读,就要选择第1种解决方案。