Spark批处理小文件合并
/**
*
* @param sparkSession
* @param absDir 要进行小文件合并的路径
* @param partitionSize 分块的大小(一般 128 ,即 128M )
* @param isDeleteBak 是否删除备份路径数据,1:删除;0:不删除(为确保安全,一般手动删除)
*/
def mergeFile(sparkSession: SparkSession, absDir: String, partitionSize: String, isDeleteBak: String): Unit = {
// 文件备份路径
val tmpDir = "/tmp/HdfsMergeSmallFile"
val hdfs = FileSystem.get(sparkSession.sparkContext.hadoopConfiguration)
val path = new Path(absDir)
if (hdfs.exists(path)) {
val contentSummary = hdfs.getContentSummary(path)
// 路径大小
val fileLength = contentSummary.getLength
// 文件数量
val fileCount = contentSummary.getFileCount
// 分区数量
val numPartitions = math.ceil(fileLength / (partitionSize.toFloat * 1024 * 1024)).toInt
// 目录数量
val dirCount = contentSummary.getDirectoryCount
/**
* 安全起见,不能对存在子路径的路径进行处理。
* 如,路径结构
* /tmp/test1/test2/a.parquet
* /tmp/test1/b.parquet
* absDir = /tmp/test1 会报错,因为存在子路径 test2。
* 这样的设定是为了防止误传参数导致整个根目录的数据被覆盖了。
* 若传参 absDir = /tmp/test1,由于test1下存在b.parquet,故不会报错。
* 代码 sparkSession.read.parquet(absDir) 会将a.parquet读取然后输出到备份路径下。
* 然后读取备份路径再覆盖写入absDir的路径,即会将absDir下的所有内容都删除。这就会存在风险隐患。
*/
if (dirCount == 1l) { // dirCount == 1l 为本路径
// numPartitions + 1 和 fileCount - 1 是因为需要排除“_SUCCESS”文件的影响
if (numPartitions + 1 >= fileCount) {
println(s"路径[${absDir}]的分区数合理。目前路径大小为[${fileLength}]B,分区数为[${fileCount - 1}],计划调整的分区数为[${numPartitions}]。")
} else {
println(s"路径[${absDir}]的分区数不合理。目前路径大小为[${fileLength}]B,分区数为[${fileCount - 1}],计划调整的分区数为[${numPartitions}]。")
// 备份的临时路径
val outputDir = tmpDir+absDir
println(s"正在写入到备份的临时路径[${outputDir}]。")
sparkSession.read.parquet(absDir).repartition(numPartitions).write.mode(SaveMode.Overwrite).parquet(outputDir)
println(s"正在覆盖写入路径[${absDir}]。")
sparkSession.read.parquet(outputDir).write.mode(SaveMode.Overwrite).parquet(absDir)
// 1 删除备份的临时路径
if ("1".equals(isDeleteBak)) {
hdfs.delete(new Path(outputDir), true)
println(s"已删除备份的临时路径[${outputDir}]。")
}
println(s"合并结束")
}
} else {
System.err.println(s"路径[${absDir}]下还存在子路径,请检查参数,或先删除子路径。")
// System.exit(1)
}
} else {
System.err.println(s"路径[${absDir}]不存在。")
// System.exit(1)
}
}