在AWS Glue的目录中,我有一个外部表,其中的分区在S3中看起来大致如此,并且每天都会添加新日期的分区:
s3://my-data-lake/test-table/
2017/01/01/
part-0000-blah.csv.gz
.
.
part-8000-blah.csv.gz
2017/01/02/
part-0000-blah.csv.gz
.
.
part-7666-blah.csv.gz
我怎样才能使用Glue / Spark将其转换为按日期划分并且每天分割为 n 文件的拼花地板?这些示例不包括分区或拆分或配置(有多少节点和多大)。每天包含几百GB。
因为源CSV不一定在正确的分区(错误的日期)并且大小不一致,所以我希望用正确的分区和更一致的大小写入分区镶木地板。
答案 0 :(得分:7)
由于源CSV文件不一定是正确的日期,您可以向他们添加有关收集日期时间的其他信息(或使用任何已有的日期):
{"collectDateTime": {
"timestamp": 1518091828,
"timestampMs": 1518091828116,
"day": 8,
"month": 2,
"year": 2018
}}
然后,您的作业可以在输出DynamicFrame中使用此信息,并最终将它们用作分区。一些如何实现此目的的示例代码:
from awsglue.transforms import *
from pyspark.sql.types import *
from awsglue.context import GlueContext
from awsglue.utils import getResolvedOptions
import sys
import datetime
###
# CREATE THE NEW SIMPLIFIED LINE
##
def create_simplified_line(event_dict):
# collect date time
collect_date_time_dict = event_dict["collectDateTime"]
new_line = {
# TODO: COPY YOUR DATA HERE
"myData": event_dict["myData"],
"someOtherData": event_dict["someOtherData"],
"timestamp": collect_date_time_dict["timestamp"],
"timestampmilliseconds": long(collect_date_time_dict["timestamp"]) * 1000,
"year": collect_date_time_dict["year"],
"month": collect_date_time_dict["month"],
"day": collect_date_time_dict["day"]
}
return new_line
###
# MAIN FUNCTION
##
# context
glueContext = GlueContext(SparkContext.getOrCreate())
# fetch from previous day source bucket
previous_date = datetime.datetime.utcnow() - datetime.timedelta(days=1)
# build s3 paths
s3_path = "s3://source-bucket/path/year={}/month={}/day={}/".format(previous_date.year, previous_date.month, previous_date.day)
# create dynamic_frame
dynamic_frame = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options={"paths": [s3_path]}, format="json", format_options={}, transformation_ctx="dynamic_frame")
# resolve choices (optional)
dynamic_frame_resolved = ResolveChoice.apply(frame=dynamic_frame,choice="project:double",transformation_ctx="dynamic_frame_resolved")
# transform the source dynamic frame into a simplified version
result_frame = Map.apply(frame=dynamic_frame_resolved, f=create_simplified_line)
# write to simple storage service in parquet format
glueContext.write_dynamic_frame.from_options(frame=result_frame, connection_type="s3", connection_options={"path":"s3://target-bucket/path/","partitionKeys":["year", "month", "day"]}, format="parquet")
没有测试它,但脚本只是如何实现这一点的一个示例,并且非常简单。
更新
1)至于在输出分区中具有特定文件大小/数字,
Spark的coalesce and repartition功能尚未在Glue的Python API中实现(仅限Scala)。
您可以将动态帧转换为数据框,并利用Spark的partition capabilities。
根据“partition_col”转换为数据框和分区
partitioned_dataframe = datasource0.toDF()。repartition(1)
转换回DynamicFrame进行进一步处理。
partitioned_dynamicframe = DynamicFrame.fromDF(partitioned_dataframe, glueContext,“partitioned_df”)
好消息是Glue有一个有趣的功能,如果每个分区有超过50,000个输入文件,它会自动将它们分组给你。
如果您想要专门设置此行为而不考虑输入文件编号(您的情况),您可以在“从选项创建动态帧”时设置以下connection_options
:< / p>
dynamic_frame = glueContext.create_dynamic_frame.from_options(connection_type="s3", connection_options={"paths": [s3_path], 'groupFiles': 'inPartition', 'groupSize': 1024 * 1024}, format="json", format_options={}, transformation_ctx="dynamic_frame")
在前面的示例中,它会尝试将文件分组为1MB组。
值得一提的是,这与 coalesce 不同,但如果你的目标是减少每个分区的文件数量,它可能会有所帮助。
2)如果目的地中已存在文件,它是否会安全地添加(不会覆盖或删除)
write_dynamic_frame.from_options
的Glue默认 SaveMode 是追加。
将DataFrame保存到数据源时,如果已经存在数据/表 存在,DataFrame的内容应该附加到 现有数据。
3)鉴于每个源分区可能是30-100GB,DPU的指南是什么
恐怕我无法回答这个问题。这取决于它加载输入文件的速度(大小/数量),脚本的转换等等。
答案 1 :(得分:0)
导入日期时间库
import datetime
根据分区条件分割时间戳
now=datetime.datetime.now()
year= str(now.year)
month= str(now.month)
day= str(now.day)
currdate= "s3:/Destination/"+year+"/"+month+"/"+day
在writer类的路径地址中添加变量currdate。结果将是镶木地板文件。