Apache spark 如何减少使用AWS胶水将拼花地板文件写入s3所需的时间

Apache spark 如何减少使用AWS胶水将拼花地板文件写入s3所需的时间,apache-spark,amazon-s3,pyspark,aws-glue,Apache Spark,Amazon S3,Pyspark,Aws Glue,我正在创建一个粘合作业,该作业需要处理来自s3路径的4TB每日数据量-s3://。因此,我创建了一个循环,通过每小时一次的文件夹(每个155Gb)将数据读入spark df,对某些类别进行筛选,并将其作为拼花文件写入s3,这些拼花文件按筛选的类别进行分区(s3:///category=/year=/month=/day=/hour=///code>)。我使用60个G2.X工作节点,每个节点都有8个vCPU、32 GB内存和128 GB磁盘。S3写入速度非常慢,需要10个多小时才能完成运行。除了增

我正在创建一个粘合作业,该作业需要处理来自s3路径的4TB每日数据量-
s3://
。因此,我创建了一个循环,通过每小时一次的文件夹(每个155Gb)将数据读入spark df,对某些类别进行筛选,并将其作为拼花文件写入s3,这些拼花文件按筛选的类别进行分区(
s3:///category=/year=/month=/day=/hour=///code>)。我使用60个G2.X工作节点,每个节点都有8个vCPU、32 GB内存和128 GB磁盘。S3写入速度非常慢,需要10个多小时才能完成运行。除了增加节点数量外,是否有其他方法加快/优化s3写入


def s3_load_job(input_list):

    hour, year, month, day = input_list
    logger.info(f"hour in s3 func {hour}")
    
    # get data from s3
    s3_path = f"s3://<path>/{year}/{month}/{day}/{hour}/"
    logger.info(f"print s3 path {s3_path}")

    #user defined library function that return spark df
    df = get_df_from_s3(glueContext, s3_path)

    df = df.withColumn('category', F.lower(F.col('category')))

    df_rep = df.where(F.col('category').isin({ "A", "B", "C","D"}))

    #write to s3
    datasink4 = DynamicFrame.fromDF(df_rep, glueContext, "datasink4")
    
    glueContext.write_dynamic_frame.from_options(frame = datasink4,
                                                             connection_type = "s3",
                                                             connection_options = 
                                                             {"path":"s3://<path>/"
                                           ,"partitionKeys"["category","year","month","day","hour"]}
                                                             ,format = "glueparquet" )



def main():
    
    year = '2020'
    month = '08'
    day = '01'
    hours = ["%.2d" % i for i in range(24)]

    input_list = [[hour, year, month, day] for hour in hours]
    logger.info(f"input_list {input_list}")

    for i in input_list:
        s3_load_job(i)
    
    job.commit()



if __name__ == "__main__":
    main()            
       


def s3_加载_作业(输入_列表):
小时、年、月、日=输入\列表
logger.info(f“s3函数中的小时数{hour}”)
#从s3获取数据
s3_path=f“s3://{year}/{month}/{day}/{hour}/”
info(f“打印s3路径{s3_路径}”)
#返回spark df的用户定义库函数
df=从_s3获取_df_(glueContext,s3路径)
df=df.withColumn('category',F.lower(F.col('category'))
df_rep=df.where(F.col('category').isin({“A”、“B”、“C”、“D”}))
#写入s3
datasink4=DynamicFrame.fromDF(df_rep,glueContext,“datasink4”)
glueContext.write_dynamic_frame.from_options(frame=datasink4,
连接类型=“s3”,
连接\u选项=
{“路径”:“s3://”
,“分区键”[“类别”、“年”、“月”、“日”、“小时”]}
,format=“glueparquet”)
def main():
年份='2020'
月份='08'
日期='01'
小时数=[“%.2d”%i表示范围内的i(24)]
输入_list=[[小时、年、月、日]表示小时,单位为小时]
info(f“input_list{input_list}”)
对于输入列表中的i:
s3装载作业(i)
job.commit()
如果名称=“\uuuuu main\uuuuuuuu”:
main()
如果使用S3(对象存储),请尝试设置以下配置:

spark.hadoop.mapreduce.fileoutputcommitter.cleanup-failures.ignored -> true
mapreduce.fileoutputcommitter.algorithm.version -> 2
如果使用S3(对象存储),请尝试设置以下配置:

spark.hadoop.mapreduce.fileoutputcommitter.cleanup-failures.ignored -> true
mapreduce.fileoutputcommitter.algorithm.version -> 2

你可以试试下面的方法

  • 不要将pyspark df转换为dynamicFrame,因为您可以直接将pyspark数据帧保存到s3
  • 由于每个文件的大小为1MB到15MB,因此需要进行优化。因此,在将数据帧写入s3之前,请尝试重新划分数据帧
  • 如果分区大小为250 GB,则应创建大小至少为256 MB的输出文件;如果是G2.x,则还可以创建大小各为512 MB的文件

    要做到这一点,你可以这样做

    您可以在每个分区中生成500个文件,格式为
    500*512=250 GB

    df.repartition(500,partitionCol).write.partitionBy(partitionCol).parquet(path)
    

    你可以试试下面的方法

  • 不要将pyspark df转换为dynamicFrame,因为您可以直接将pyspark数据帧保存到s3
  • 由于每个文件的大小为1MB到15MB,因此需要进行优化。因此,在将数据帧写入s3之前,请尝试重新划分数据帧
  • 如果分区大小为250 GB,则应创建大小至少为256 MB的输出文件;如果是G2.x,则还可以创建大小各为512 MB的文件

    要做到这一点,你可以这样做

    您可以在每个分区中生成500个文件,格式为
    500*512=250 GB

    df.repartition(500,partitionCol).write.partitionBy(partitionCol).parquet(path)
    

    看来你一定找到了解决办法。 想和大家分享一下对我有用的东西。我每小时运行一次胶水作业,启用作业书签以不重新处理旧文件。确保您没有创建过多的分区,这不仅会导致执行时间延长,而且如果您希望通过Athena进行查询,那么从长远来看,您的查询可能会超时。将分区保持在最小值。通过重新分区,您的作业可能会花费太多时间来洗牌数据,从而增加作业运行时间。 然而,频繁的每小时跑步会有所帮助。
    一定要分享对你有用的东西。

    看来你一定找到了处理这个问题的方法。 想和大家分享一下对我有用的东西。我每小时运行一次胶水作业,启用作业书签以不重新处理旧文件。确保您没有创建过多的分区,这不仅会导致执行时间延长,而且如果您希望通过Athena进行查询,那么从长远来看,您的查询可能会超时。将分区保持在最小值。通过重新分区,您的作业可能会花费太多时间来洗牌数据,从而增加作业运行时间。 然而,频繁的每小时跑步会有所帮助。
    请分享对您有用的内容。

    目标中的平均文件大小是多少?是否超过256MB的数据块?经过过滤后,每个要写入的类别的大小都不同(倾斜),“A”大约为600GB,“B”400GB,其余各为250G。写入的目标文件的大小是通过glue(优化为使用所有cpu核)内部计算的,据我所见,每个文件的大小范围在1-15MB之间。在将动态帧写入s3时,我不使用任何合并/重新分区来避免内存错误。目标中的平均文件大小是多少?是否超过256MB的数据块?经过过滤后,每个要写入的类别的大小都不同(倾斜),“A”大约为600GB,“B”400GB,其余各为250G。写入的目标文件的大小是通过glue(优化为使用所有cpu核)内部计算的,据我所见,每个文件的大小范围在1-15MB之间。在将动态帧写入s3时,我不使用任何合并/重新分区来避免内存错误。谢谢,请您解释一下上述代码如何影响s3写入优化?对于一致性模型意味着基于重命名的提交是安全的对象存储(例如s3),请使用v.2算法提高性能。与v.1算法相比,它在作业结束时进行的重命名更少。由于它仍然使用rename()提交文件,因此当对象存储没有一致的元数据/列表时使用它是不安全的。提交者也可以设置为