如何将Spark Job处理S3文件优化为Hive Parquet Table

时间:2019-01-21 04:26:00

标签: apache-spark dataframe hive apache-spark-sql rdd

我是Spark分布式开发的新手。我正在尝试优化现有的Spark作业,该作业最多需要1个小时才能完成。

基础结构

  • EMR [10个r4.8xlarge实例(32核,244GB)]
  • 源数据:S3中的1000个.gz文件(每个〜30MB)
  • 火花执行参数[执行器:300,执行器内存:6gb,内核:1]

通常,Spark作业执行以下操作:

private def processLines(lines: RDD[String]): DataFrame = {
    val updatedLines = lines.mapPartitions(row => ...)
    spark.createDataFrame(updatedLines, schema)
}

// Read S3 files and repartition() and cache()
val lines: RDD[String] = spark.sparkContext
    .textFile(pathToFiles, numFiles) 
    .repartition(2 * numFiles) // double the parallelism
    .cache()

val numRawLines = lines.count()

// Custom process each line and cache table
val convertedLines: DataFrame = processLines(lines)
convertedRows.createOrReplaceTempView("temp_tbl")
spark.sqlContext.cacheTable("temp_tbl")
val numRows = spark.sql("select count(*) from temp_tbl").collect().head().getLong(0)

// Select a subset of the data
val myDataFrame = spark.sql("select a, b, c from temp_tbl where field = 'xxx' ")

// Define # of parquet files to write using coalesce
val numParquetFiles = numRows / 1000000
var lessParts = myDataFrame.rdd.coalesce(numParquetFiles)
var lessPartsDataFrame = spark.sqlContext.createDataFrame(lessParts, myDataFrame.schema)
lessPartsDataFrame.createOrReplaceTempView('my_view')

// Insert data from view into Hive parquet table
spark.sql("insert overwrite destination_tbl 
           select * from my_view")    
lines.unpersist()

应用程序读取所有S3文件=>重分区到文件数量的两倍=>缓存RDD =>每行的自定义过程=>创建临时视图/缓存表=>计算行数=>选择一个子集数据=>减少分区数量=>创建数据子集的视图=>使用视图=>插入到配置单元目标表=>持久保留RDD。

我不确定为什么要花很长时间才能执行。火花执行参数设置不正确还是在这里调用不正确?

1 个答案:

答案 0 :(得分:1)

在查看指标之前,我将尝试对您的代码进行以下更改。

private def processLines(lines: DataFrame): DataFrame = {
  lines.mapPartitions(row => ...)
}

val convertedLinesDf = spark.read.text(pathToFiles)
    .filter("field = 'xxx'")
    .cache()

val numLines = convertedLinesDf.count() //dataset get in memory here, it takes time        
// Select a subset of the data, but it will be fast if you have enough memory
// Just use Dataframe API
val myDataFrame = convertedLinesDf.transform(processLines).select("a","b","c")

//coalesce here without converting to RDD, experiment what best
myDataFrame.coalesce(<desired_output_files_number>)
  .write.option(SaveMode.Overwrite)
  .saveAsTable("destination_tbl")
  • 如果不计算行数,则缓存是无用的。而且会占用一些内存并增加一些GC压力
  • 缓存表可能会占用更多内存并增加GC压力
  • 将数据帧转换为RDD的成本很高,因为它暗示着ser / deser操作
  • 不确定要使用的内容:val numParquetFiles = numRows / 1000000和重新分区(2 * numFiles)。通过设置,每个1000MB的文件(每个30MB)将为您提供1000个分区。这样可能很好。调用重新分区和合并可能会触发改组操作,这很昂贵。 (Coalesce可能不会触发洗牌)

如果有任何改善,请告诉我!