无法在athena中加载区分大小写的分区

esyap4oy  于 2021-06-24  发布在  Hive
关注(0)|答案(1)|浏览(488)

我有s3中的数据,它在 YYYY/MM/DD/HH/ 结构(非 year=YYYY/month=MM/day=DD/hour=HH )
我为此设置了一个glue crawler,它在athena中创建了一个表,但是当我在athena中查询数据时,它会给出一个错误,因为一个字段有重复的名称( URL 以及 url ,serde将其转换为小写,从而导致名称冲突)。
为了解决这个问题,我手动创建了另一个表(使用上面的表定义showcreatetable),添加 'case.insensitive'= FALSE 到SERDEPROPERTY

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

我将s3目录结构更改为与配置单元兼容的命名 year=/month=/day=/hour= 然后用 'case.insensitive'= FALSE ,然后运行 MSCK REPAIR TABLE 用于加载所有分区的新表的命令(完成创建表查询)
但是在查询时,我只能找到一个数据列( platform )对于分区列,所有列的其余部分都不进行解析。但我实际上复制了glue生成的create table查询 case_insensitive=false 条件。

我怎样才能解决这个问题?

jogvjijk

jogvjijk1#

我认为您有多个独立的问题:一个是爬虫,一个是serde,还有一个是重复的密钥:

胶水爬行器

如果glue crawler兑现了他们的承诺,那么对于大多数情况来说,它们将是一个相当好的解决方案,并且可以避免我们反复编写相同的代码。不幸的是,如果您偏离了glue crawler设计的(未记录的)用例,那么您经常会遇到各种问题,从奇怪到完全崩溃(例如,请参阅这个问题、这个问题、这个问题、这个问题、这个问题或这个问题)。
我建议您跳过glue crawler,而是手工编写表ddl(crawler创建的模板很好,但还不够好)。然后编写一个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 property它按预期工作,这让我想到它可能不会像你想象的那样工作。我试着把它调成 TRUE 而不是 FALSE ,这使得table又像预期的那样工作了。我想我们可以由此得出结论,雅典娜文档说“默认情况下,雅典娜要求json数据集中的所有键都使用小写”是错误的。事实上,athena在读取json时会降低列名的大小写,但也会降低属性名的大小写。
经过进一步的实验,结果证明 path 财产也是多余的。这张table对我很有用:

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 列(as) struct<url:string> )并补充道 "pageData":{"URL":"URL","url":"url"} 根据数据,我得到了一个错误:
配置单元\光标\错误:行不是有效的json对象-jsonexception:重复键“url”
不管 pagedata 列是否参与查询(例如。 SELECT userid FROM json_case_test 也出错了)。我试过了 case.insensitive serde属性 TRUE 以及 FALSE ,但没有效果。
接下来,我看了一下serde的源文档,首先它的措辞要好得多,其次它包含了一个关键信息:当您关闭不区分大小写时,还需要为列提供Map。
通过以下serde属性,我可以消除重复密钥问题:

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

您必须为所有列提供Map,除了 platform ,也是。

替代方法:使用json函数

您在对这个答案的评论中提到 pageData 属性不是常量。不幸的是,这是另一个胶水爬虫无法真正工作的情况。如果你运气不好,你会得到一个包含一些属性的flapping模式(参见这个问题的例子)。
当我看到你的评论时,我意识到还有另一种解决方法:手动设置表(如上所述)并使用 string 作为 pagedata 列。然后你可以使用如下函数 JSON_EXTRACT_SCALAR 在查询期间提取所需的属性。
这个解决方案用增加的查询复杂性来换取更少的麻烦来跟上不断发展的模式。

相关问题