Spark Java写入Excel时partitionBy不生效,如何实现类CSV的分区存储?
根因说明
你碰到的写入异常,本质是Scala 2.11 线对应的早期com.crealytics.spark-excel版本,写入侧根本没完整实现Spark原生的分区写入协议:
- 没有复用Spark内置的动态生成分区目录、自动剔除分区列的FileCommitter流程
- 会直接把
.save()传的路径识别为单个Excel文件的输出目标,因此会把本来要生成的sales文件夹直接覆盖成同名单文件 - 因为压根没进入分区写入流程,自然不会自动拆分区目录、也不会帮你剔除分区列
对齐CSV分区表现的实现方案
你现在用的版本下,链式调用的.partitionBy()对Excel写入完全无效,不要依赖这个方法。直接通过手动按分区值遍历写入的方式,就能100%复刻CSV的输出效果:
操作逻辑很简单:
- 先按
date列重分区,把同一分区的数据集中到同一个分片,避免反复拉取全量数据 - 拿出所有去重后的
date值,按照Spark分区的标准格式date=分区值拼接子目录路径 - 每个分区单独过滤出对应数据,把
date列drop掉,和CSV写入的列裁剪逻辑完全对齐 - 逐分区把数据写成Excel存到对应子目录
可直接复用的Java代码实现:
import static org.apache.spark.sql.functions.col; import static org.apache.spark.sql.functions.lit; import org.apache.spark.sql.SaveMode; // 按date列重分区,确保同分区数据集中 Dataset<Row> partitionedDs = outputDataset.repartition(1, col("date")); // 获取全量去重的分区值 List<Row> distinctPartitions = partitionedDs.select(col("date")).distinct().collectAsList(); for (Row partitionRow : distinctPartitions) { String dateValue = partitionRow.getAs("date").toString(); // 过滤当前分区数据,剔除分区列,列结构和CSV输出完全对齐 Dataset<Row> currentData = partitionedDs .filter(col("date").equalTo(lit(dateValue))) .drop("date"); // 写入对应分区子目录,和CSV生成的路径结构完全一致 currentData.write() .format("com.crealytics.spark.excel") .option("header", "true") .mode(SaveMode.ErrorIfExists) .save(String.format("/saveLoc/sales/date=%s", dateValue)); }
输出效果匹配校验
按上面的逻辑写完,最终的输出和你用CSV写的效果没有任何区别:
- 根路径是
/saveLoc/sales文件夹 - 文件夹下生成
date=2021-04-14的分区子目录 - 子目录里的Excel文件只保留
col_A、col_B两列,和CSV的列结构完全一致 - 不会再出现把
sales文件夹覆盖成单文件的问题
注意点
- 这套实现完全适配你当前用的Scala 2.11版本的spark-excel、Spark core依赖,不需要升级组件
- 后续读这个路径下的数据时,Spark能自动识别
date=xxx格式的分区目录,自动关联上date分区列,和原生分区表的读取逻辑完全一致
内容的提问来源于stack exchange,提问作者KCK
相关产品推荐
相关产品推荐

