0

客观的

我有一个 S3 文件夹,里面装满了不同模式的 json 文件,包括数组(一个 dynamodb 备份,碰巧)。然而,虽然模式有所不同,但所有文件都包含一些常见元素,例如“id”或“name”,以及不同长度的嵌套数组,例如“选定项目”。我希望能够在闲暇时解析出这些元素。

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

背景

工作方法是:

  1. 使用 Spectrum 将所有 S3 数据加载为外部表,每个 json 记录作为单个varchar(65535)条目。
  2. json_extract_path_text使用 Redshift SQL 函数解析出我需要的元素,包括特定的数组,例如json_array_length
  3. 通过对数组索引的引用表进行交叉连接来规范化所需的 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 数据 - 根据这篇文章,Glue 的解析器启发式方法决定源的各种模式差异太大而无法与单个源相关,因此将它们解析为一堆不同的表。有一个爬虫只需爬取每个文件以生成一个表,该表具有类型为 varchar(65535) 的单列,每行包含一个 json 条目,但似乎没有任何 Glue 分类器 JSON 路径表达式可以实现这一目标。
  2. 'single varchar(65535) column' 方法可以通过将数据加载为 Spectrum 中的外部表来实现,但似乎 Spectrum 表无法作为动态帧加载到 Glue 中(请注意,相关表存在于 Glue 目录中它显示为具有预期的 varchar(65535) 架构)。在 Zeppelin Notebooks 工作,我发现

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

    运行成功,但是会生成一个带有newFrame.count() = 0和的表newFrame.toDF().show(n),对于任何 n 值都会生成以下形式的奇数输出:

    ++ || ++ ++

    简而言之,pySpark 似乎无法通过 Glue 直接使用 Spectrum 表。

  3. 使用 Crawler 抓取 Spectrum 表。在这里,我通过与我的 Redshift 集群的 Glue 连接将爬虫指向了所需的表。但是,这会导致 S3 端点验证失败,我还不能解决这个问题。我不愿意深入研究 VPC 配置,因为我已经非常不确定将 Crawler 指向 Spectrum 表是否是正确的方法。

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

问题

所以我的问题是,在一个声明中:有什么方法可以使用 Glue 解析 S3 上具有不一致架构的非标量 json 文件(另外,可能还有其他 AWS 服务,例如 RS Spectrum)?

4

1 回答 1

1

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

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

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

如果由于任何原因,以前的方法似乎不起作用,那么胶水作业会有所帮助。作为此处的说明 - 始终只使用 spark,永远不要使用任何与glue_context 相关的东西。无论如何,在 spark.read.blaah 你可以指定一个模式参数。用它。

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

这样你会得到相同的结果——它只会解析出你想要的数据。

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

  • S3 输入存储桶具有 JSON 文件
  • 胶水作业从输入存储桶读取数据并写入不同的存储桶,可能已分区和/或以不同的数据格式
  • 胶水表定义在第二个桶/前缀之上
  • Redshift Spectrum 指向包含上一步中定义的表的数据库
于 2018-06-27T07:19:58.220 回答