如何使用apache spark处理数百万个较小的s3文件

时间:2017-02-16 23:10:35

标签: amazon-web-services hadoop apache-spark amazon-s3

所以这个问题一直让我感到疯狂,并且开始觉得s3的火花不是这项特定工作的正确工具。基本上,我在s3存储桶中有数百万个较小的文件。由于我无法进入的原因,这些文件无法合并(一个是唯一的加密转录本)。我见过类似的问题,每一个解决方案都没有产生好的结果。我尝试的第一件事是外卡:

sc.wholeTextFiles(s3aPath + "/*/*/*/*.txt").count();

注意:计数更多地调试处理文件所需的时间。这项工作几乎花了一整天时间超过10个实例,但仍未通过列表底部发布的错误。然后我找到了这个链接,它基本上说这不是最优的:https://forums.databricks.com/questions/480/how-do-i-ingest-a-large-number-of-files-from-s3-my.html

然后,我决定尝试另一种我目前无法找到的解决方案,即加载所有路径,然后合并所有路径

    ObjectListing objectListing = s3Client.listObjects(bucket);
    List<JavaPairRDD<String, String>> rdds = new ArrayList<>();
    List<JavaPairRDD<String, String>> tempMeta = new ArrayList<>();
    //initializes objectListing
    tempMeta.addAll(objectListing.getObjectSummaries().stream()
            .map(func)
            .filter(item -> item != null && item.getMediaType().equalsIgnoreCase("transcript"))
            .map(item -> SparkConfig.getSparkContext().wholeTextFiles("s3a://" + bucket + "/" + item.getFileName()))
            .collect(Collectors.toList()));

    while(objectListing.isTruncated()) {
        objectListing = s3Client.listNextBatchOfObjects(objectListing);
        tempMeta.addAll(objectListing.getObjectSummaries().stream()
                .map(func)
                .filter(item -> item != null && item.getMediaType().equalsIgnoreCase("transcript"))
                .map(item -> SparkConfig.getSparkContext().wholeTextFiles("s3a://" + bucket + "/" + item.getFileName()))
                .collect(Collectors.toList()));
        if (tempMeta.size() > 5000) {
            rdds.addAll(tempMeta);
            tempMeta = new ArrayList<>();
        }
    }

    if (!tempMeta.isEmpty()){
        rdds.addAll(tempMeta);
    }
    return SparkConfig.getSparkContext().union(rdds.get(0), rdds.subList(1, rdds.size()));

然后,即使我设置emrfs-site配置为:

{
    "Classification": "emrfs-site",
    "Properties": {
      "fs.s3.consistent.retryPolicyType": "fixed",
      "fs.s3.consistent.retryPeriodSeconds": "15",
      "fs.s3.consistent.retryCount": "20",
      "fs.s3.enableServerSideEncryption": "true",
      "fs.s3.consistent": "false"
    }
}

每次尝试运行工作后的6小时内我都会收到此错误:

17/02/15 19:15:41 INFO AmazonHttpClient: Unable to execute HTTP request: randomBucket.s3.amazonaws.com:443 failed to respond
org.apache.http.NoHttpResponseException: randomBucket.s3.amazonaws.com:443 failed to respond

首先,有没有办法使用来自s3的火花的较小文件?我不在乎解决方案是不是最理想的,我只是想尝试让事情变得有效。我考虑尝试火花流,因为它的内部与加载所有文件有点不同。然后我会使用fileStream并将newFiles设置为false。然后我可以批量处理它们。然而,这并不是火花流的构建方式,所以我在走这条路时会有冲突。

作为旁注,我在hdfs中生成了数百万个小文件,并尝试了相同的工作,并在一小时内完成。这让我觉得它具体是s3。另外,我使用的是s3a,而不是普通的s3。

1 个答案:

答案 0 :(得分:9)

如果您使用的是亚马逊EMR,那么您需要使用s3:// URL; s3a://用于ASF版本。

一个大问题是在s3中列出目录树需要多长时间,尤其是递归树遍历。 spark代码假定它是一个快速的文件系统,其中列出dirs和stating文件是低成本的,而实际上每个操作需要1-4个HTTPS请求,即使在重用的HTTP / 1.1连接上也会受到伤害。 can see the pauses in the log可能会很慢。

这真的很痛苦的是它是前期分区,其中发生了很多延迟,所以这是连续工作的序列化工作正在瘫痪。

虽然在作为the S3a phase II work的一部分的Hadoop 2.8中,S3a的树上行走有一些加速,但 / / * .txt形式的通配符扫描不会获得任何加速。我的建议是尝试展平您的目录结构,以便您从深树移动到浅层,甚至可能在同一目录中,以便可以在没有步行的情况下扫描它,每5000个条目需要1个HTTP请求

请记住,无论如何,许多小文件都非常昂贵,包括在HDFS中,它们会占用存储空间。有一种特殊的聚合格式,HAR文件,就像tar文件,除了hadoop,hive和spark都可以在文件本身内部工作。 可能有帮助,但我没有看到任何实际的性能测试数据。