避免使用 hadoop (EMR) 在 S3 中创建 _$folder$ 键

Avoid creation of _$folder$ keys in S3 with hadoop (EMR)

我在 AWS 数据管道中使用 EMR Activity。这个 EMR Activity 是 运行 EMR 集群中的一个配置单元脚本。它以 dynamo DB 作为输入并将数据存储在 S3 中。

这是 EMR Activity

中使用的 EMR 步骤
s3://elasticmapreduce/libs/script-runner/script-runner.jar,s3://elasticmapreduce/libs/hive/hive-script,--run-hive-script,--hive-versions,latest,--args,-f,s3://my-s3-bucket/hive/my_hive_script.q,-d,DYNAMODB_INPUT_TABLE1=MyTable,-d,S3_OUTPUT_BUCKET=#{output.directoryPath}

其中

out.direcoryPath 是:

s3://my-s3-bucket/output/#{format(@scheduledStartTime,"YYYY-MM-dd")}

这样就在S3中创建了一个文件夹和一个文件。 (从技术上讲,它创建了两个键 2017-03-18/<some_random_number>2017-03-18_$folder$

2017-03-18
2017-03-18_$folder$

如何避免创建这些额外的空 _$folder$ 文件。

编辑: 我在 https://issues.apache.org/jira/browse/HADOOP-10400 找到了一个解决方案,但我不知道如何在 AWS 数据管道中实施它。

S3 中无法实际创建空文件夹。 S3 是一个对象存储,所以里面的一切都是一个对象。

当 Hadoop 将其用作文件系统时,它需要组织这些对象以使其显示为文件系统树,因此它会创建一些特殊对象来将对象标记为目录。

你只是存储数据文件,但你可以选择将这些数据文件组织到路径中,这就产生了一个类似于文件夹的概念来遍历。

包括 AWS 管理控制台在内的一些工具通过解释对象名称中的 /s 来模拟文件夹。 Amazon S3 控制台支持将文件夹概念作为对对象进行分组的一种方式。 Bucket Explorer 也是如此。

如果您只是不创建文件夹,而是将文件放在您想要的路径中 - 这应该适合您。

在 S3 中写入文件之前不必创建文件夹,因为 /all/path/including/filename - 是 S3 存储中的整个密钥。

EMR 似乎没有提供避免这种情况的方法。

Because S3 uses a key-value pair storage system, the Hadoop file system implements directory support in S3 by creating empty files with the "_$folder$" suffix.

You can safely delete any empty files with the <directoryname>_$folder$ suffix that appear in your S3 buckets. These empty files are created by the Hadoop framework at runtime, but Hadoop is designed to process data even if these empty files are removed.

https://aws.amazon.com/premiumsupport/knowledge-center/emr-s3-empty-files/

它在 Hadoop 源代码中,因此可以修复,但显然它在 EMR 中没有修复。

如果您觉得自己聪明,可以创建一个与 _$folder$ 后缀匹配的 S3 事件通知,并让它触发 Lambda 函数以在对象创建后将其删除。

在 EMR bootstrap 操作中使用以下脚本来解决此问题。 AWS 提供的补丁

#!/bin/bash

# NOTE: This script replaces the s3-dist-cp RPM on EMR versions 4.6.0+ with s3-dist-cp-2.2.0.
# This is intended to remove the _$folder$ markers when creating the destination prefixes in S3.

set -ex

RPM=bootstrap-actions/s3-dist-cp-2.2.0/s3-dist-cp-2.2.0-1.amzn1.noarch.rpm

LOCAL_DIR=/var/aws/emr/packages/bigtop/s3-dist-cp/noarch

# Get the region from metadata
REGION=$(curl http://169.254.169.254/latest/meta-data/placement/availability-zone/ 2>/dev/null | head -c -1)

# Choose correct bucket for region
if [ $REGION = "us-east-1" ]
then
    BUCKET=awssupportdatasvcs.com
else
    BUCKET=$REGION.awssupportdatasvcs.com
fi

# Download new RPM
sudo rm $LOCAL_DIR/s3-dist-cp*.rpm
aws s3 cp s3://$BUCKET/$RPM /tmp/
sudo cp /tmp/s3-dist-cp-2.2.0-1.amzn1.noarch.rpm $LOCAL_DIR/

echo Rebuilding Repo
sudo yum install -y createrepo
sudo createrepo --update -o /var/aws/emr/packages/bigtop /var/aws/emr/packages/bigtop
sudo yum clean all

在写入 s3 存储桶时使用 s3a,它将删除 $folder$。我测试过这种胶水。不确定它是否适用于 EMR 集群。

来源:- 某人在 reddit 上的回答

from pyspark.sql import SparkSession
spark=SparkSession.builder.getOrCreate()
df=spark.read.format("parquet").load("s3://testingbucket/")
df.write.format("parquet").save("s3a://testingbucket/parttest/")
spark.stop()