AWS Glue ETL Job 增量加载数据

转载自:https://aws.amazon.com/blogs/big-data/load-data-incrementally-and-optimized-parquet-writer-with-aws-glue/

AWS Glue提供了一个无服务器环境来准备(提取和转换)和加载来自各种来源的大量数据集,以便使用 Apache Spark ETL 作业进行分析和数据处理。本系列的第一篇博文使用 AWS Glue 扩展 Apache Spark 作业和分区数据的最佳实践,讨论了帮助 Apache Spark 应用程序和 Glue ETL 作业的开发人员、大数据架构师、数据工程师和业务分析师扩展数据处理的最佳实践自动在 AWS Glue 上运行的作业。

这篇博文展示了如何使用 JDBCAmazon S3数据湖和数据库中的数据源增量加载数据它还展示了如何通过使用作业书签仅读取新添加的数据来扩展 AWS Glue ETL 作业,并通过将作业书签重置为先前作业运行的结尾来处理迟到的数据。这篇博文还回顾了使用带有复杂 AWS Glue ETL 脚本和工作负载的作业书签的最佳实践。

最后,该博文展示了如何通过避免额外的数据传递并在运行时计算架构来使用针对性能进行了优化的自定义 AWS Glue Parquet 编写器。AWS Glue Parquet 编写器还允许通过添加或删除列在数据集中进行架构演变。

AWS Glue 作业书签

AWS Glue 的 Spark 运行时有一种存储状态的机制。此机制用于跟踪由 ETL 作业的特定运行处理的数据。持久化的状态信息称为作业书签

上面的快照显示了 Glue 控制台的视图,其中多个作业在同一 ETL 作业的不同时间实例运行。AWS Glue 作业使用作业书签来处理自上次作业运行以来的增量数据。作业书签由各种作业元素的状态组成,例如源、转换和目标。例如,您的 AWS Glue 作业可能会读取 S3 支持的表中的新分区。AWS Glue 会跟踪作业已成功处理的分区,以防止重复处理并将相同数据多次写入目标数据存储。

作业书签 API

使用 AWS Glue 控制台或 AWS Glue API 启动作业时,作业书签选项作为参数传递。

有以下三种可能的选择:

  • 启用- 此选项使作业在每次成功运行后更新书签状态以跟踪处理的数据。在同一数据源上运行的后续作业仅处理自上次检查点以来新添加的数据。
  • 禁用- 确保不使用可能导致作业始终处理整个数据集的作业书签。这是默认选项。
  • 暂停- 读取状态信息并处理自上次检查点以来的增量数据,但不更新它。您可以使用此选项,以便每个后续运行都及时处理来自同一时间点的数据。

在所有情况下,您都有责任管理先前作业的输出。有关更多信息,请参阅本系列的第一篇博文,使用 AWS Glue 扩展 Apache Spark 作业和分区数据的最佳实践有关传递给作业的参数的详细信息,特别是作业书签的详细信息,请参阅AWS Glue 使用的特殊参数

以下代码示例展示了如何在 Glue ETL 作业中使用作业书签,该作业从由 Amazon S3 位置支持的 AWS Glue 表中读取。该作业从Kinesis Firehose事件流接收JSON 格式的新文件,转换以重命名两列,转换并将其写出到Amazon Redshift。  transformation_ctx是与此数据源关联的作业书签的标识符。为了正确操作,您需要job.initjob.commit来初始化和保持书签状态。

import sys
from awsglue.transforms import *
from awsglue.utils import getResolvedOptions
from pyspark.context import SparkContext
from awsglue.context import GlueContext
from awsglue.job import Job

## @params: [JOB_NAME]
args = getResolvedOptions(sys.argv, ['JOB_NAME'])
sc = SparkContext()
glueContext = GlueContext(sc)
spark = glueContext.spark_session

job = Job(glueContext)
job.init(args['JOB_NAME'], args)
datasource0 = glueContext.create_dynamic_frame.from_catalog(database = "firehose_s3_db",
table_name = "firehose_s3_raw_table",
transformation_ctx = "datasource0")
applymapping = ApplyMapping.apply(frame = datasource0,
mappings = [("col0", "string", "name", "string"), ("col1", "string", "number", "string")],
transformation_ctx = "applymapping1")

glueContext.write_dynamic_frame.from_jdbc_conf(frame = applymapping, catalog_connection = "redshift", connection_options = {"dbtable": "name", "database": "kinesis_db"}, redshift_tmp_dir= "s3://redshift_tmp_dir_path")

job.commit()

Python


使用 API 或 CLI 启动作业运行时,您需要添加以下参数以启用作业书签:



Job Arguments :

--job-bookmark-option, job-bookmark-enable
--JOB_NAME, glue-job-incremental

代码


对于 S3 输入源,AWS Glue 作业书签会检查对象的上次修改时间以验证要重新处理哪些对象。如果有来自 Kinesis firehose 的新文件,或自上次作业运行以来现有文件发生更改,则在使用定期 Glue 作业触发器S3 触发器通知再次运行作业时,将重新处理这些文件


如果您打算使用同一作业重新处理所有数据,请重置作业书签。要重置作业书签状态,请使用 AWS Glue 控制台、ResetJobBookmark 操作(Python:reset_job_bookmark) API 操作或 AWS CLI。例如,使用 AWS CLI 输入以下命令:


aws glue reset-job-bookmark --job-name my-job-name


您还ResetJobBookmark API可以通过传入作业运行 ID 将 用于计划作业运行的特定点。完成后,它将作业书签的状态重置为作业运行 ID 之后的状态。这个功能类似于时间旅行;例如,您现在可以重新处理过去某个时间的输入数据,并在您的 ETL 脚本或下游作业中使用一组不同的转换,这些转换是通过 ETL 管道中的AWS Glue 工作流程编排的在 AWS Glue 控制台中,您可以使用回滚作业书签选项将作业书签状态重置为先前作业运行的提交。



AWS Glue 会跟踪每个作业的书签。如果您删除一个作业,您也会删除该作业书签。流行的基于 S3 的存储格式,包括 JSON、CSV、Apache Avro、XML 和 JDBC 源,支持作业书签。AWS Glue 1.0 版开始,还支持列式存储格式,例如 Apache Parquet 和 ORC。


最佳实践 1:使用作业书签进行开发


在某些情况下,您可能会启用 AWS Glue 作业书签,但您的 AWS Glue 作业会重新处理它在之前运行中已经处理过的数据。这可能是由于以下原因造成的:



  • 缺少作业提交job.commit()AWS Glue ETL 脚本末尾的语句更新作业书签的状态。如果不包括它,作业将重新处理以前处理过的文件和新文件。确保作业提交语句由用户脚本中所有可能的代码路径执行,从而导致作业完成。

  • 缺少转换上下文——转换上下文是一个可选参数,GlueContext但是,作业书签需要它才能正常运行。确认在创建 DynamicFrame时包含转换上下文参数请参阅以下代码示例:

    sample_dynF=glueContext.create_dynamic_frame_from_catalog(database,
    table_name,
    transformation_ctx="sample_dynF")
    Python


  • JDBC 源– 作业书签要求源表具有主键列 [s] 或具有递增值的列 [s],当您使用 JDBC 连接访问关系数据库时,需要在源选项中指定这些值。作业书签只能捕获新添加的行。此行为不适用于存储在 S3 上的源表。

  • 上次修改时间– 为了确定要处理哪些存储在 S3 上的文件,作业书签会检查对象的上次修改时间,而不是文件名。如果您的输入对象自上次运行作业以来发生了更改,则在作业再次运行时会重新处理它们。


最佳实践 2:监控作业书签


有三种方法可以检查任何作业运行的作业书签的行为:



  • 文件列表存储在 tmp 目录中– 所有运行 Apache Spark 并使用 DynamicFrames 读取数据的 AWS Glue ETL 作业输出一个清单文件,其中包含每个路径的已处理文件列表。清单文件存储在作业指定的临时位置。文件的路径是:<temporary location>/partitionlisting/<job name>/<run id>/<source transformation_ctx>.input-files.json该文件捕获为相应数据源读取的文件列表,而不管是否启用了任何作业书签。

  • 作业指标– 您可以使用 AWS Glue 作业指标来检查 S3 读写操作并使用书签跟踪作业读取的字节数。您还可以在 AWS Glue 控制台中跟踪作业在其多次运行中读取的数据。有关更多信息,请参阅监控多个作业的进度

  • Glue 作业日志– AWS Glue 作业还会在 Spark 驱动程序日志流中发出与处理和跳过 S3 中的分区相关的日志。日志存储在Amazon CloudWatch 中


跳过分区


当作业为空或 AWS Glue 数据目录中该特定分区的创建时间戳早于作业书签捕获的上次作业运行的时间戳时,作业将跳过分区。以下示例日志消息指示跳过的分区:



19/05/21 14:49:22 WARN HadoopDataSource: Skipping Partition
{"year": "2019", "month": "03", "day": "26", "hour": "13"}
has no new files detected
@ s3://input-s3-prefix/Year=2019/Month=03/Day=26/Hour=13/
or path does not exist
代码


处理分区


当作业发现在上次作业运行后创建的新 S3 分区或有新文件要处理时,它会生成一条日志消息。日志消息还指示特定分区中文件总数的百分比。当前作业运行的初始和最终作业书签过滤器处理这些文件。以下示例说明了作业书签过滤逻辑。


如果分区是新的(根据分区创建时间在最近的作业运行后创建),则作业会处理分区中的所有文件。分区创建时间为 1559235148000,即上次作业运行之后。请参阅以下示例日志消息:



19/05/31 10:39:55 INFO PartitionFilesListerUsingBookmark:
Found new partition DynamicFramePartition(com.amazonaws.services.glue.DynamicRecord@35309577,
s3://input-s3-prefix/Year=2018/Month=12/Day=05/Hour=13/,
1559235148000)
with 47 files
代码


现有分区触发第一个书签过滤器。此过滤器选择自上次作业运行以来具有修改时间戳的文件。在以下示例日志消息中,分区中的 47 个文件中有 15 个是新文件,应进行处理:



19/05/31 10:40:31 INFO PartitionFilesListerUsingBookmark:
After initial job bookmarks filter,
processing 31.91% of 47 files
in partition DynamicFramePartition(com.amazonaws.services.glue.DynamicRecord@aa39e364,
s3://input-s3-prefix/Year=2018/Month=12/Day=05/Hour=13/,
1559235148000)
代码


最后的书签过滤器执行额外的过滤,以避免与 S3 的最终一致性相关的竞争条件。如果大量文件以相同的修改时间到达,则此过滤器可能会将它们排除在处理之外。在以下示例日志消息中,过滤器处理了初始书签过滤器捕获的所有 15 个文件:



19/05/31 10:50:31 INFO PartitionFilesListerUsingBookmark:
After final job bookmarks filter, processing 100.00% of 15 files
in partition DynamicFramePartition(com.amazonaws.services.glue.DynamicRecord@35309577,
s3://input-s3-prefix/Year=2018/Month=12/Day=05/Hour=13/,
1559235148000)
代码


优化的 Apache Parquet 编写器


当使用 DynamicFrames 来提高性能时,AWS Glue 提供优化的 Apache Parquet 编写器。Apache Parquet 格式的读取速度通常比写入速度快,因为它的列式存储布局和预先计算的模式将数据写入文件。AWS Glue 的 Parquet 编写器提供快速的写入性能和灵活性来处理不断发展的数据集。与默认的 Apache Spark Parquet 编写器不同,它不需要预先计算的架构或通过对输入数据集执行额外扫描来推断的架构。


您可以通过将函数format参数设置为 来启用 AWS Glue Parquet 编写器当数据通过 AWS Glue 作业流式传输以写入 S3 时,优化的写入器会在运行时动态计算和合并架构,从而加快作业运行时间。AWS Glue Parquet 编写器还支持删除和添加新列,从而实现架构演变。write_dynamic_frame.from_optionsglueparquet


您可以通过设置format_options参数进一步调整 AWS Glue Parquet 编写器请参阅以下代码示例:



block_size = 12810241024
page_size = 1024*1024
glueContext.write_dynamic_frame.from_options(frame = dyFrame,
connection_type = "s3", connection_options = {"path": output_dir},
format = "glueparquet",
format_options = {"compression": "snappy",
blockSize = block_size, pageSize = page_size})
Python


format_options 的默认值如下:



  • compression“snappy”

  • blockSize 是 128 MB

  • pageSize 是 1 MB


blockSize 指定缓存在内存中的 Parquet 文件中行组的大小。pageSize 指定 Parquet 文件中必须完全读取才能访问单个记录的最小单元的大小。


结论


这篇博文讨论了 AWS Glue 作业书签如何帮助增量处理从 S3 和关系数据库收集的数据。您还了解了如何使用作业书签使回填历史数据变得简单。与工作书签交互很容易;您可以启用、禁用、暂停和倒回它们到之前的时间点。通过监控作业书签的进度和状态,您可以更好地调整作业并构建检查以确保所有数据都得到正确处理。


您还可以使用 AWS Glue Parquet 编写器来优化在数据湖中写入 Apache Parquet 文件的性能。优化的编写器支持 Parquet 文件的架构演变,因此您可以自动管理对数据的更改。


我们希望您尝试使用这些功能在 AWS Glue 上的 Apache Spark 应用程序中加载和写入数据。


本系列的第三篇博文讨论 AWS Glue 的自动代码生成功能如何让您轻松处理和转换复杂的数据集。该博文还展示了如何直接从您的 AWS Glue ETL 脚本对您的数据集执行 SQL 查询,以及如何使用 AWS Glue 工作流程安排和编排数据管道。


 




关于作者


Mohit Saxena 是 AWS Glue 的技术主管经理他热衷于构建可扩展的分布式系统,以有效管理云上的数据。他还喜欢看电影和阅读最新技术。


 


 


 


Bijay Bisht 是 AWS 的高级软件开发工程师。


 


 


posted @ 2021-08-10 09:31  Jerry-1  阅读(770)  评论(0编辑  收藏  举报