如何在没有丢失信息的情况下将Spark etl更多地并行化(文件名中)

时间:2018-08-15 18:11:17

标签: scala apache-spark hdfs

我要逐一查看HDFS上的文件列表,将其作为文本打开,然后保存回HDFS,到另一个位置。正在解析数据,然后合并 part 文件并将其保存为原始名称,后缀为BZIP2。但是,它相当慢-每个文件大约需要3s,而每个文件夹有10,000多个。我不确定如何保存文件名信息,因此需要逐个文件查找。我需要名称才能执行MD5,并“确认”没有发生任何信息丢失。

这是我的代码:

import org.apache.hadoop.fs.{FileSystem, Path, FileUtil}
import org.apache.spark.deploy.SparkHadoopUtil
import org.apache.spark.sql._ 
import org.apache.spark.sql.functions._ 
import org.apache.spark.sql.functions.broadcast 
import org.apache.spark.sql.types._ 
import org.apache.spark.{SparkConf, SparkContext} 

sc.getConf.set("spark.hadoop.mapred.output.compress", "true")
sc.getConf.set("spark.hadoop.mapred.output.compression.codec", "true")
sc.getConf.set("spark.hadoop.mapred.output.compression.codec", 
               "org.apache.hadoop.io.compress.BZip2Codec")
sc.getConf.set("spark.hadoop.mapred.output.compression.type", "BLOCK")

val hdfsConf = SparkHadoopUtil.get.newConfiguration(sc.getConf)
val hdfs = FileSystem.get(hdfsConf)
val sourcePath = new Path("/source/*20180801*") 

hdfs.globStatus(sourcePath).foreach( fileStatus => {
  val fileName = fileStatus.getPath().getName()
  val filePathName = fileStatus.getPath().toString
  if (fileName.contains(".done")) {
    /* open, then save compressed */
    val myFile = sc.textFile(filePathName)
    val compressedBasePath = "/destination/compressed/"
    /* use tmp_ to store folder w/ parts in it */
    val compressedPath = compressedBasePath + "tmp_/" + fileName
    myFile.saveAsTextFile(compressedPath, 
                          classOf[org.apache.hadoop.io.compress.BZip2Codec])
    /* merge part* -> old_name.bzip */
    FileUtil.copyMerge(hdfs, new Path(compressedPath), hdfs, 
                       new Path(compressedBasePath + "/" + fileName + ".bzip2"), 
                       true, hdfsConf, null)
    myFile.unpersist()
  }
})

在我需要测试之前,我使用了类似的方法:

val myFile = sc.textFile("/source/*20180801*")
myFile.saveAsTextFile(compressedPath, 
                      classOf[org.apache.hadoop.io.compress.BZip2Codec])

但是我不能重命名,所以我需要名称。 有什么想法我可以做什么?

更新: 多亏了注释中的建议和this particular question,我得以使用并行集合解决此问题。唯一真正的更改是导入import scala.collection.parallel.immutable.ParVector并添加par方法调用,然后再进行foreach

有关并行集合的完整文章:https://docs.scala-lang.org/overviews/parallel-collections/overview.html

Thx

1 个答案:

答案 0 :(得分:0)

原始问题的评论中有两种可能的解决方案:

TBH,我只测试了第二种方法,因为它是一种更快的方法(警告更少)。最终解决方案只需要进行最小的更改-返回适当的lib导入和Array hdfs.globStatus(sourcePath)调用的并行化。这是最终的代码,其中删除了注释,并添加了两个注释,以便于发现更改。

import org.apache.hadoop.fs.{FileSystem, Path, FileUtil}
import org.apache.spark.deploy.SparkHadoopUtil
import org.apache.spark.sql._ 
import org.apache.spark.sql.functions._ 
import org.apache.spark.sql.functions.broadcast 
import org.apache.spark.sql.types._ 
import org.apache.spark.{SparkConf, SparkContext} 
import scala.collection.parallel.immutable.ParVector /* added */

sc.getConf.set("spark.hadoop.mapred.output.compress", "true")
sc.getConf.set("spark.hadoop.mapred.output.compression.codec", "true")
sc.getConf.set("spark.hadoop.mapred.output.compression.codec", 
               "org.apache.hadoop.io.compress.BZip2Codec")
sc.getConf.set("spark.hadoop.mapred.output.compression.type", "BLOCK")

val hdfsConf = SparkHadoopUtil.get.newConfiguration(sc.getConf)
val hdfs = FileSystem.get(hdfsConf)
val sourcePath = new Path("/source/*20180801*") 

/* note the par method call below */
hdfs.globStatus(sourcePath).par.foreach( fileStatus => {
  val fileName = fileStatus.getPath().getName()
  val filePathName = fileStatus.getPath().toString
  if (fileName.contains(".done")) {
    val myFile = sc.textFile(filePathName)
    val compressedBasePath = "/destination/compressed/"
    val compressedPath = compressedBasePath + "tmp_/" + fileName
    myFile.saveAsTextFile(compressedPath, 
                          classOf[org.apache.hadoop.io.compress.BZip2Codec])
    FileUtil.copyMerge(hdfs, new Path(compressedPath), hdfs, 
                       new Path(compressedBasePath + "/" + 
                                fileName.replace(".done", ".done.bz2")), 
                       true, hdfsConf, null)
    myFile.unpersist()
  }
})