Spark 2.2创建带分区Hive表报错:ALTER TABLE RECOVER PARTITIONS仅适用于指定位置表
我之前在Spark 2.2版本也遇到过一模一样的问题,当时排查了很久才找到根源——这其实是Spark 2.2在处理内部分区表时的一个小bug。
问题根源
当你使用partitionBy结合saveAsTable时,Spark会自动尝试执行ALTER TABLE RECOVER PARTITIONS来同步Hive元数据和文件系统中的分区。但这个命令只对显式指定了存储路径(location)的外部表生效,而saveAsTable默认创建的是Hive内部表(managed table),这类表的存储路径由Hive自动管理,没有显式指定location,所以触发了这个报错。
虽然报错了,但Spark已经完成了表结构创建、分区目录生成和数据写入,只是元数据同步失败,导致Hive无法识别到分区里的数据,所以你能看到Parquet文件但查不到内容。
可行的解决方案
方案1:显式指定表的存储路径(转为外部表)
在写入时通过option("path", ...)指定表的存储路径,这样Spark会创建外部表,RECOVER PARTITIONS命令就能正常执行了:
result.write .mode(SaveMode.Overwrite) .option("path", "/user/hive/warehouse/db.db/resultTable") .partitionBy("year", "month", "day") .saveAsTable("db.resultTable")
方案2:先创建Hive表结构,再用insertInto写入
这种方式绕开Spark自动执行的RECOVER PARTITIONS操作,先在Hive中手动定义表结构:
CREATE TABLE db.resultTable ( -- 替换成你的实际列定义,比如 id int, name string... ) PARTITIONED BY (year int, month int, day int) STORED AS PARQUET;
然后在Spark中用insertInto写入数据:
result.write .mode(SaveMode.Overwrite) .partitionBy("year", "month", "day") .insertInto("db.resultTable")
方案3:升级Spark版本(推荐)
这个bug在Spark 2.3及以后的版本中已经被修复了。如果你的环境允许,升级到Spark 2.3+后,直接用你原来的代码就能正常工作,不需要额外配置。
补充说明
你之前配置的hive.exec.dynamic.partition和hive.exec.dynamic.partition.mode参数是没问题的,它们控制的是Hive动态分区的行为,和这个报错没有直接关系。
内容的提问来源于stack exchange,提问作者RudyVerboven

