如何使用Spark捆绑S3中的许多文件

时间:2014-02-17 01:29:41

标签: scala hadoop amazon-s3 apache-spark

我在S3中有2000万个文件,大约持续8000天。

文件按UTC时间戳组织,如下所示:s3://mybucket/path/txt/YYYY/MM/DD/filename.txt.gz。每个文件都是UTF-8文本,包含0(空)和100KB文本(第95百分位,尽管有一些文件最多可达几MB)。

使用Spark和Scala(我是两个新手并且想要学习),我想保存“每日捆绑”(其中​​8000个),每个包含当天发现的任意数量的文件。理想情况下,我想存储原始文件名及其内容。输出也应该驻留在S3中并以某种格式压缩,适合在进一步的Spark步骤和实验中输入。

一个想法是将bundle存储为一堆JSON对象(每行一个'\n' - 分开),例如。

{id:"doc0001", meta:{x:"blah", y:"foo", ...}, content:"some long string here"}
{id:"doc0002", meta:{x:"foo", y:"bar", ...}, content: "another long string"}

或者,我可以尝试Hadoop SequenceFile,但我不知道如何优雅地设置它。

以Spark shell为例,我看到它很容易读取文件,例如:

val textFile = sc.textFile("s3n://mybucket/path/txt/1996/04/09/*.txt.gz")
// or even
val textFile = sc.textFile("s3n://mybucket/path/txt/*/*/*/*.txt.gz")
// which will take for ever

但是如何“拦截”读者提供文件名?

或许我应该得到所有文件的RDD,按日拆分,并在缩小步骤中写出K=filename, V=fileContent

3 个答案:

答案 0 :(得分:4)

您可以使用此

首先,您可以获得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

答案 1 :(得分:2)

你尝试过sc.wholeTextFiles吗?

它创建一个RDD,其中键是文件名,值是整个文件的字节数组。然后你可以映射这个,所以键是文件日期,然后是groupByKey?

http://spark.apache.org/docs/latest/programming-guide.html

答案 2 :(得分:1)

按照您的规模,优雅的解决方案将是一个延伸。

我建议不要永远使用sc.textFile("s3n://mybucket/path/txt/*/*/*/*.txt.gz")。你可以做的是使用AWS DistCp或类似的东西将文件移动到HDFS。一旦进入HDFS,以任何适合您的方式摄取信息的火花都非常快。

请注意,大多数这些进程都需要某种文件列表,因此您需要以某种方式生成它。对于20密耳的文件,这个文件列表的创建将是瓶颈。我建议创建一个附加文件路径的文件,每次将文件上传到s3。

输出相同,放入hdfs然后转到s3(虽然直接复制可能同样有效)。