AWS Glue:如何使用不同的模式来ETL非标量JSON

时间:2018-06-26 07:12:32

标签: amazon-web-services amazon-s3 amazon-dynamodb aws-glue amazon-redshift-spectrum

客观

我有一个S3文件夹,里面充满了具有不同模式(包括数组)的json文件,包括数组(动态备份)。但是,尽管架构有所不同,但所有文件都包含一些通用元素,例如'id'或'name',以及不同长度的嵌套数组,例如'selected items'。我希望能够在闲暇时解析这些元素。

我有一个使用外部ETL工具(KNIME)的可行方法,我希望通过Glue以无服务器方式复制该工具。

背景

工作方法是:

  1. 使用Spectrum将所有S3数据作为外部表加载,每个 json记录为单个varchar(65535)条目。
  2. 使用json_extract_path_textjson_array_length之类的Redshift SQL函数解析出我需要的元素,包括特定的数组
  3. 需要归一化 数组cross joining against a reference table的json数组 索引
  4. 执行必需的表联接并写出给Redshift Tableau的消费

现在,这似乎是应该适合Glue的任务。即,我想做的是:

  1. 如上所述,使用Spectrum加载所有数据

  2. 在Glue中,从Spectrum表中创建一个动态框架

  3. 使用pyspark.sql.functions.explode()之类的功能,或者使用Glue的Relationalize变换,从上述动态帧中解析数据

或者:

  1. 将所有标量数据抓取到单个Glue模式中(假设Glue尚不支持JSON数组)
  2. 使用上述方法之一解析JSON并爆炸数组

到目前为止的结果

很遗憾,我无法使用这两种方法。对于各种方法,阻止者已成为:

  1. 使用Glue抓取json数据-根据this post,Glue的解析器启发式方法确定源的各种模式差异太大而无法与单个源相关,因此将它们解析为一堆不同的表。只要有一个搜寻器就可以简单地搜寻每个文件,以生成一个表,该表具有类型为varchar(65535)的单列,每行包含一个json条目,但似乎没有任何Glue分类器JSON路径表达式实现这一目标。
  2. “单个varchar(65535)列”方法可以通过在Spectrum中将数据作为外部表加载来实现,但是似乎Spectrum表不能作为动态帧加载到Glue中(请注意,相关的该表存在于Glue目录中,其中显示为具有预期的varchar(65535)模式)。在Zeppelin笔记本电脑上工作时,我发现了

    newFrame = glueContext.create_dynamic_frame.from_catalog(database="<spectrum database>", table_name="<spectrum one column varchar table>")

    运行成功,但是生成一个包含newFrame.count() = 0newFrame.toDF().show(n)的表,因为n的任何值都会产生以下形式的奇数输出:

    ++ || ++ ++

    简而言之,看来pySpark无法通过Glue直接与Spectrum表一起使用。

  3. 使用“搜寻器”搜寻“频谱”表。在这里,我已经通过与我的Redshift集群的Glue连接将搜寻器指向了所需的表。但是,这使我无法解决S3端点验证失败。鉴于我已经非常不确定是否将Crawler指向Spectrum表,因此我不愿深入研究VPC配置。

简而言之,我发现没有任何方法可以使用Glue Crawler或Glue和Redshift Spectrum的组合在S3上动态提取和解析非标量json。可以说这不是一项深奥的任务-实际上,任何想要相对自动化的方法来报告基于dynamodb的Web应用程序中的数据的人都需要实现这一目标。

问题

所以我的问题只用一条语句:是否有任何方法可以使用Glue(加上可能的其他AWS服务,例如RS Spectrum)来解析具有不一致模式的S3上的非标量json文件?

1 个答案:

答案 0 :(得分:1)

所以我认为在后台发生了一些事情。

我假设您在Redshift Spectrum中定义了一个指向S3的外部表?如果是这样,那不是最好的方法。而是定义一个指向Glue数据目录中的表的外部模式。结果是RS Spectrum将在该Glue数据库中看到所有内容,而无需定义单独的表。

其次,您是否尝试过在Glue数据目录中手动定义表?我已经使用Parquet文件格式进行了广泛的测试,弄乱了表定义和文件内容。结果是,无论从该表中查询什么,它都只会返回表中定义的数据。因此,您可以定义一个具有“ id”,“ name”和“ selected items”字段的表,其他所有内容都将被忽略。

如果由于某种原因前一个命令似乎不起作用,则可以执行胶粘作业。这里要注意-始终只使用spark,不要使用任何与gele_context相关的东西。无论如何,您可以在spark.read.blaah中指定一个模式参数。使用它。

dataSchema = StructType([StructField("id",StringType(),True) ,StructField("name",StringType(),True) ,StructField("selected items",ArrayType(.. etc ...),True) ])

这样,您将获得相同的结果-它只会解析出您想要的数据。

最后的设置可能应该是这样的:

  • S3输入存储桶具有JSON文件
  • 胶水作业从输入存储桶中读取数据,并将其写入可能已分区和/或使用不同数据格式的其他存储桶
  • 胶水表定义在第二个存储桶/前缀的顶部
  • Redshift Spectrum指向包含上一步中定义的表的数据库