【问题标题】:Unable to load partitions in Athena with case sensitivity ON无法在区分大小写的情况下在 Athena 中加载分区
【发布时间】:2020-05-12 18:05:54
【问题描述】:

我在 S3 中有数据,这些数据以 YYYY/MM/DD/HH/ 结构(不是 year=YYYY/month=MM/day=DD/hour=HH)进行分区

我为此设置了一个 Glue 爬虫,它在 Athena 中创建了一个表,但是当我在 Athena 中查询数据时,它给出了一个错误,因为一个字段具有重复的名称(URLurl,SerDe 将其转换小写,导致名称冲突)。

为了解决这个问题,我手动创建另一个表(使用上面的表定义 SHOW CREATE TABLE),将'case.insensitive'= FALSE 添加到 SERDEPROPERTIES

WITH SERDEPROPERTIES ('paths'='deviceType,emailId,inactiveDuration,pageData,platform,timeStamp,totalTime,userId','case.insensitive'= FALSE) 

我将 s3 目录结构更改为与 hive 兼容的命名 year=/month=/day=/hour=,然后使用 'case.insensitive'= FALSE 创建表,然后为新表运行 MSCK REPAIR TABLE 命令,它会加载所有分区。 (Complete CREATE TABLE QUERY)

但在查询时,我只能找到 1 个数据列 (platform) 和分区列,其余所有列都没有解析。但我实际上复制了 Glue 生成的 CREATE TABLE 查询,带有case_insensitive=false 条件。

我该如何解决这个问题?

【问题讨论】:

  • 在您链接到的表 DDL 以及您问题的 serde 属性中,没有 URL 或 url 列/属性?这里似乎有两个不同的问题。你能澄清一下你得到的两个版本的错误吗?
  • @Theo 我已经更新了问题的详细信息。此外,URL 字段不存在,因为我从查询中删除了它,即数据可能是敏感的,对此感到抱歉。粘合生成的查询和修改后的查询之间的唯一区别是添加了 case_insensitive=false
  • @Theo url 和 URL 字段实际上在 pagedata 列内,这是一个嵌套的 JSON

标签: amazon-s3 hive amazon-athena aws-glue-data-catalog


【解决方案1】:

我认为您有多个单独的问题:一个与爬虫有关,一个与 serde 有关,另一个与重复键有关:

胶水爬行器

如果 Glue Crawler 兑现了他们的承诺,它们在大多数情况下将是一个相当不错的解决方案,并且可以避免我们一遍又一遍地编写相同的代码。不幸的是,如果您偏离了 Glue Crawler 设计的(未记录的)用例,您通常会遇到各种问题,从奇怪到完全损坏(参见例如this questionthis questionthis questionthis questionthis questionthis question)。

我建议您跳过 Glue Crawler,而是手动编写 DDL 表(您在爬虫创建的内容中有一个很好的模板,只是还不够好)。然后,您编写一个按计划运行的 Lambda 函数(或 shell 脚本)以添加新分区。

由于您的分区只是按时进行,因此这是一个相当简单的脚本:它只需要每隔一段时间运行一次并为下一个时期添加分区。

您的数据似乎来自 Kinesis Data Firehose,它以小时为单位生成分区结构。除非您每小时都有大量数据,否则我建议您创建一个仅按日期分区的表,并每天运行一次 Lambda 函数或脚本以添加第二天的分区。

不使用 Glue Crawler 的一个好处是您不必在路径组件和分区键之间建立一一对应的关系。您可以拥有一个键入为date 的分区键,并像这样添加分区:ALTER TABLE foo ADD PARTITION (dt = '2020-05-13') LOCATION 's3://some-bucket/data/2020/05/13/'。这很方便,因为在完整日期上进行范围查询比在组件分开时要容易得多。

如果你真的需要每小时粒度,你可以有两个分区键,一个是日期,一个是小时,或者只有一个带有完整时间戳的,例如ALTER TABLE foo ADD PARTITION (ts = '2020-05-13 10:00:00') LOCATION 's3://some-bucket/data/2020/05/13/10/'。然后每小时运行一次 Lambda 函数或脚本,添加下一小时的分区。

过细的分区对性能没有帮助,反而会损害性能(尽管性能损失主要来自小文件和目录)。

SerDe 配置

之所以只看到platform 列的值,是因为它是唯一一种列名和属性大小写相同的情况。

您链接到的 DDL 不起作用有点令人惊讶,但我可以确认它确实不起作用。我尝试从该 DDL 创建一个表,但没有 pagedata 列(我也跳过了分区,但这对测试没有影响),实际上只有 platform 列在我查询时有任何值桌子。

但是,当我删除 case.insensitive serde 属性时,它按预期工作,这让我想到它可能不会像你想象的那样工作。我尝试将其设置为TRUE 而不是FALSE,这使得表格再次按预期工作。我认为我们可以由此得出结论,当 Athena 文档说“默认情况下,Athena 要求 JSON 数据集中的所有键都使用小写”时,它是错误的。事实上,Athena 将列名小写,但在读取 JSON 时,它也将属性名小写。

经过进一步的实验,发现path 属性也是多余的。这是一张适合我的表格:

CREATE EXTERNAL TABLE `json_case_test` (
  `devicetype` string, 
  `timestamp` string, 
  `totaltime` string, 
  `inactiveduration` int, 
  `emailid` string, 
  `userid` string, 
  `platform` string
)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 
STORED AS INPUTFORMAT 'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://some-bucket/data/'

我想说case.insensitive 造成的问题似乎比它解决的问题多。

重复键

当我添加pagedata 列(如struct<url:string>)并将"pageData":{"URL":"URL","url":"url"} 添加到数据中时,我得到了错误:

HIVE_CURSOR_ERROR:行不是有效的 JSON 对象 - JSONException:重复键“url”

无论pagedata 列是否参与查询,我都会收到错误消息(例如SELECT userid FROM json_case_test 也有错误)。我用TRUEFALSE 尝试了case.insensitive serde 属性,但没有效果。

接下来,我查看了the source documentation for the serde,首先它的措辞更好,其次包含关键信息:当您关闭不区分大小写时,您还需要为列提供映射。

使用以下 serde 属性,我能够解决重复密钥问题:

WITH SERDEPROPERTIES (
  "case.insensitive" = "false",
  "mapping.pagedata" = "pageData",
  "mapping.pagedata.url" = "pagedata.url",
  "mapping.pagedata.url2"= "pagedata.URL"
)

您还必须为除platform 之外的所有列提供映射。


替代方案:使用 JSON 函数

您在对此答案的评论中提到 pageData 属性的架构不是恒定的。这是另一种情况,不幸的是,Glue Crawlers 并没有真正起作用。如果你不走运,你最终会得到一个包含一些属性的浮动模式(例如参见this question)。

当我看到您的评论时,我意识到您的问题还有另一种解决方案:手动设置表(如上所述)并使用string 作为pagedata 列的类型。然后您可以使用JSON_EXTRACT_SCALAR 之类的函数在查询期间提取您想要的属性。

此解决方案以查询的复杂性增加为代价换取了更少的麻烦来跟上不断发展的架构。

【讨论】:

  • 我已提议更新 Athena 文档以阐明该属性的工作原理:github.com/awsdocs/amazon-athena-user-guide/pull/46
  • ……它很快就被合并了,文档的误导性会降低!
  • 感谢西奥的帮助。但是,这对于根级别的列效果很好,并且还填充了嵌套的 JSON 列,但是里面的数据大部分是空的,不管我是否保留 URL 和 url 映射,数据几乎都是空的,大部分的 NULL 值键/嵌套键。
  • 我认为在映射中定义所有可能的字段几乎是不可能的,因为其中一些可能是动态/新的。我想我会通过预处理数据然后运行胶水爬虫来摆脱重复的 URL。感谢您的帮助。
  • @Yankee 根据您的评论,我为我的答案添加了替代解决方案。即使您可以删除重复的 URL 属性,但如果您的架构变化太大,这可能还不够。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-06-26
  • 1970-01-01
  • 2019-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多