我有一个每天都有分区的Athena表,其中实际文件按小时在“子目录”中,如下所示:
s3://my-bucket/data/2019/06/27/00/00001.json
s3://my-bucket/data/2019/06/27/00/00002.json
s3://my-bucket/data/2019/06/27/01/00001.json
s3://my-bucket/data/2019/06/27/01/00002.json
Athena可以毫无问题地查询该表并找到我的数据,但是在使用AWS Glue时,它似乎无法找到该数据。
ALTER TABLE mytable ADD
PARTITION (year=2019, month=06, day=27) LOCATION 's3://my-bucket/data/2019/06/27/01';
select day, count(*)
from mytable
group by day;
day . count
27 . 145431
我已经尝试过将分区的位置更改为以斜杠(s3://my-bucket/data/2019/06/27/01/
)结尾,但这无济于事。
以下是Glue中的分区属性。我希望storedAsSubDirectories设置可以告诉它迭代子目录,但事实并非如此:
{
"StorageDescriptor": {
"cols": {
"FieldSchema": [
{
"name": "userid",
"type": "string",
"comment": ""
},
{
"name": "labels",
"type": "array<string>",
"comment": ""
}
]
},
"location": "s3://my-bucket/data/2019/06/27/01/",
"inputFormat": "org.apache.hadoop.mapred.TextInputFormat",
"outputFormat": "org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat",
"compressed": "false",
"numBuckets": "0",
"SerDeInfo": {
"name": "JsonSerDe",
"serializationLib": "org.openx.data.jsonserde.JsonSerDe",
"parameters": {
"serialization.format": "1"
}
},
"bucketCols": [],
"sortCols": [],
"parameters": {},
"SkewedInfo": {
"skewedColNames": [],
"skewedColValues": [],
"skewedColValueLocationMaps": {}
},
"storedAsSubDirectories": "true"
},
"parameters": {}
}
当Glue在相同的分区/表上运行时,它将发现0行。
但是,如果所有数据文件都出现在分区的根“目录”中(即s3://my-bucket/data/2019/06/27/00001.json),则Athena和Glue都可以找到数据。
是否有某些原因导致Glue无法找到数据文件?我不希望每个小时都创建一个分区,因为那将意味着每年8700个分区(而Athena的每个表限制为20,000个分区)。
答案 0 :(得分:0)
AWS Glue数据目录应该用于定义有关实际数据的元信息,例如表模式,分区的位置等。分区的概念是限制Athena仅扫描S3存储桶中某些目标的方法,以提高速度和成本效益。当使用Athena查询S3存储桶中的数据时,它会使用Glue数据目录中指定的表定义。这也意味着,当您在Athena中执行DDL语句时,将在Glue datacatalog中创建相应的表。所以我不确定“胶水发现0行”
是什么意思如果您使用Athena这样创建表格:
CREATE EXTERNAL TABLE `mytable`(
`labels` array<string>,
`userid` string)
PARTITIONED BY (
`year` string,
`month` string,
`day` string,
`hour` string)
ROW FORMAT SERDE
'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
'paths'='labels,userid,')
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
's3://my-bucket/data/'
请注意,LOCATION
指向分区开始的位置。然后添加一个分区应该像这样:
ALTER TABLE mytable
ADD PARTITION (year=2019, month=06, day=27, hour=00)
LOCATION 's3://my-bucket/data/2019/06/27/00/';
ALTER TABLE mytable
ADD PARTITION (year=2019, month=06, day=28, hour=00)
LOCATION 's3://my-bucket/data/2019/06/28/00/';
在这两个DDL查询语句之后,您应该可以在mytable
标签下的两个分区的Glue数据目录中看到View partitions
。现在,如果您运行的查询中没有WHERE
语句:
SELECT
"day", COUNT(*)
FROM
mytable
GROUP BY "day";
然后将扫描分区指定的所有数据,您应该得到
| day | count |
|-----|----------------|
| 27 | some number |
| 28 | another number |
现在,如果您要计算特定日期内的记录,则需要添加WHERE
语句
SELECT
"day", COUNT(*)
FROM
mytable
WHERE(
"day" = '27'
)
GROUP BY "day";
然后将仅扫描s3://my-bucket/data/2019/06/27/
下的数据,您应该得到类似以下内容:
| day | count |
|-----|----------------|
| 27 | some number |
s3://my-bucket/data/year=2019/month=06/day=27/hour=00/
,则在定义表后,您只需运行MSCK REPAIR TABLE mytable
,所有分区将被添加到Glue中的表中数据目录。对于大量分区,运行ALTER TABLE mytable ADD PARTITION ...
是不可行的。相反,您可以使用:
对于Athena client,您可以将ALTER TABLE mytable ADD PARTITION ...
语句生成为字符串,然后将其发送执行。这是一个post on Medium,可以帮助您入门。
您还可以使用Glue client通过batch_create_partition
或create_partition
方法执行相同的操作,但这将需要与Athena客户程序不同的输入
如果您的数据具有类似结构
s3://my-bucket/data/2019/06/27/00/00001.json
s3://my-bucket/data/2019/06/27/00/00002.json
s3://my-bucket/data/2019/06/27/01/00001.json
s3://my-bucket/data/2019/06/27/01/00002.json
...
s3://my-bucket/data/2019/06/28/00/00001.json
s3://my-bucket/data/2019/06/28/00/00002.json
s3://my-bucket/data/2019/06/28/01/00001.json
s3://my-bucket/data/2019/06/28/01/00002.json
但是您只希望只有3个分区,即年,月,日,那么表的定义应考虑到这一点:
CREATE EXTERNAL TABLE `mytable`(
`labels` array<string>,
`userid` string)
PARTITIONED BY ( -- Here we specify only three columns
`year` string,
`month` string,
`day` string)
ROW FORMAT SERDE
'org.openx.data.jsonserde.JsonSerDe'
WITH SERDEPROPERTIES (
'paths'='labels,userid,')
STORED AS INPUTFORMAT
'org.apache.hadoop.mapred.TextInputFormat'
OUTPUTFORMAT
'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
's3://my-bucket/data/'
然后用于添加分区的DDL语句应为:
ALTER TABLE mytable
ADD PARTITION (year=2019, month=06, day=27)
LOCATION 's3://my-bucket/data/2019/06/27/'; -- Stop at day level
ALTER TABLE mytable
ADD PARTITION (year=2019, month=06, day=28)
LOCATION 's3://my-bucket/data/2019/06/28/'; -- Stop at day level
请记住,在S3中没有文件夹或目录之类的东西。这就是我在Athena,Glue和S3上下文中看到分区和位置的方式。分区是一组S3对象的抽象,其中分组是通过针对某个“前缀” <=>位置过滤所有对象来定义的。因此,当您指定LOCATION
时,请停在“日级别”。虽然,您可以在“小时级别”停下来,例如s3://my-bucket/data/2019/06/28/01/
,但是如果您希望Athena能够扫描它们,则需要在所有其他小时中创建分区。最重要的是,分区值的组合应该是唯一的(等同于定义4个分区),否则AWS不允许创建它。
只需在我的AWS账户中使用类似于您的S3路径的数据进行测试,并且能够查看Glue数据目录中指向正确目的地的分区。
答案 1 :(得分:0)
显然,create_dynamic_frame上有一个未记录的“递归”附加选项:
egrep -o ' [0-9,]*|^[0-9,]*' | tr , . | tr -d ' '
示例:
additional_options = {"recurse": True}
我刚刚使用此选项测试了我的Glue作业,可以确认现在可以找到所有s3文件。
答案 2 :(得分:0)
我也遇到过同样的情况。
我为S3桶手动创建了Glue数据目录表。该目录包含一些未分配为任何分区键的子目录。通过目录表,Athena查询甚至可以处理子目录中的所有文件。但是胶水作业create_dynamic_frame.from_catalog
却没有。将additional_options = {"recurse": True}
添加到from_catalog
,Glue Job在子目录中查找文件。
在我的情况下,目录表具有分区属性"storedAsSubDirectories" = "false"
,因为当我使用Glue控制台或Athena DDL查询创建目录表时,该属性是自动分配的,并且无法触摸控制台上的值。尽管具有此属性,但它可以与附加选项recurse=True
一起使用。我怀疑属性storedAsSubDirectories
不适用于该词的含义。
正如@ 3nochroot所说,即使在今天,似乎也没有在官方文件中声明。