YARN上的Apache Spark:大量输入数据文件(在spark中组合多个输入文件)

时间:2014-07-08 03:52:12

标签: hadoop apache-spark yarn

需要帮助实施最佳实践。 操作环境如下:

  • 日志数据文件不定期到达。
  • 日志数据文件的大小为3.9KB到8.5MB。平均约为1MB。
  • 数据文件的记录数为13行到22000行。平均约2700行。
  • 数据文件必须在聚合之前进行后处理。
  • 可以更改后处理算法。
  • 后处理文件与原始数据文件分开管理,因为后处理算法可能会更改。
  • 执行每日聚合。必须逐个记录所有后处理数据文件,并计算聚合(平均值,最大最小值...)。
  • 由于聚合是细粒度的,因此聚合后的记录数量不会太少。它可以是原始记录数量的一半左右。
  • 此时,后处理文件的数量可以是大约200,000。
  • 应该可以单独删除数据文件。

在测试中,我尝试使用带有glob路径的sc.textFile()从Spark处理160,000个后处理文件,但在驱动程序进程中出现OutOfMemory异常时失败。

处理此类数据的最佳做法是什么? 我应该使用HBase而不是普通文件来保存后处理数据吗?

3 个答案:

答案 0 :(得分:9)

我们写了自己的装载机。它通过HDFS中的小文件解决了我们的问题。它使用Hadoop CombineFileInputFormat。 在我们的例子中,它将映射器的数量从100000减少到大约3000,并使工作速度明显加快。

https://github.com/RetailRocket/SparkMultiTool

示例:

import ru.retailrocket.spark.multitool.Loaders 
val sessions = Loaders.combineTextFile(sc, "file:///test/*") 
// or val sessions = Loaders.combineTextFile(sc, conf.weblogs(), size = 256, delim = "\n") 
// where size is split size in Megabytes, delim - line break character 
println(sessions.count())

答案 1 :(得分:3)

我很确定你获得OOM的原因是因为处理了这么多小文件。你想要的是组合输入文件,这样你就不会得到这么多分区。我尝试将我的工作限制在大约10k分区。

textFile之后,你可以使用.coalesce(10000, false) ...不是100%肯定会有效,因为我已经有一段时间了,请告诉我。所以试试

sc.textFile(path).coalesce(10000, false)

答案 2 :(得分:1)

您可以使用此

首先,您可以获得一个缓冲区/ S3路径列表/ HDFS或本地路径相同

如果您正在尝试使用Amazon S3,那么:

import scala.collection.JavaConverters._
import java.util.ArrayList
import com.amazonaws.services.s3.AmazonS3Client
import com.amazonaws.services.s3.model.ObjectListing
import com.amazonaws.services.s3.model.S3ObjectSummary
import com.amazonaws.services.s3.model.ListObjectsRequest

def listFiles(s3_bucket:String, base_prefix : String) = {
    var files = new ArrayList[String]

    //S3 Client and List Object Request
    var s3Client = new AmazonS3Client();
    var objectListing: ObjectListing = null;
    var listObjectsRequest = new ListObjectsRequest();

    //Your S3 Bucket
    listObjectsRequest.setBucketName(s3_bucket)

    //Your Folder path or Prefix
    listObjectsRequest.setPrefix(base_prefix)

    //Adding s3:// to the paths and adding to a list
    do {
      objectListing = s3Client.listObjects(listObjectsRequest);
      for (objectSummary <- objectListing.getObjectSummaries().asScala) {
        files.add("s3://" + s3_bucket + "/" + objectSummary.getKey());
      }
      listObjectsRequest.setMarker(objectListing.getNextMarker());
    } while (objectListing.isTruncated());

    //Removing Base Directory Name
    files.remove(0)

    //Creating a Scala List for same
    files.asScala
  }

现在将此List对象传递给以下代码段,注意:sc是SQLContext的对象

var df: DataFrame = null;
  for (file <- files) {
    val fileDf= sc.textFile(file)
    if (df!= null) {
      df= df.unionAll(fileDf)
    } else {
      df= fileDf
    }
  }

现在你有一个最终的统一RDD,即df

可选,您也可以在一个BigRDD

中重新分区
val files = sc.textFile(filename, 1).repartition(1)

重新分区始终有效:D