2

我有一个 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 个分区)。

4

3 回答 3

6

显然,create_dynamic_frame 上有一个未记录的附加选项用于“recurse”: additional_options = {"recurse": True}

例子:

athena_datasource = glueContext.create_dynamic_frame.from_catalog(database = target_database, table_name = target_table, push_down_predicate = "(year=='2019' and month=='06' and day=='27')", transformation_ctx = "athena_datasource", additional_options = {"recurse": True})

我刚刚使用此选项测试了我的 Glue 作业,并且可以确认它现在可以找到所有 s3 文件。

于 2019-07-03T15:52:50.167 回答
0

AWS Glue 数据目录应该定义有关实际数据的元信息,例如表架构、分区位置等。分区概念是一种限制 Athena 仅扫描 S3 存储桶中的某些目标以提高速度和成本效率的方法。当您使用 Athena 查询位于 S3 存储桶中的数据时,它使用在 Glue 数据目录中指定的表定义。这也意味着,当您在 Athena 中执行 DDL 语句时,会在 Glue 数据目录中创建相应的表。所以我不确定“Glue 找到 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    |

补充说明

  • 根据 AWS 的说法,Glue 目录中的一个表最多可以有1000 万个分区,因此每年 8700 个分区几乎不是问题。
  • AWS 不会就 Athena 执行的 DDL 语句向您收费。
  • 如果您在 S3 中的路径遵循 HIVE 约定,即s3://my-bucket/data/year=2019/month=06/day=27/hour=00/在您定义表之后,您可以简单地运行MSCK REPAIR TABLE mytable所有分区都将添加到 Glue 数据目录中的表中。
  • 对于大量分区,运行是不可行的ALTER TABLE mytable ADD PARTITION ...。相反,您可以使用:

    1. 胶水爬行器。根据我的经验,这仅在您对数据知之甚少且大量数据时才有用。这是AWS 定价
    2. AWS 开发工具包,例如用于 python的boto3 。它为 Athena 和 Glue 客户端提供 API。

    对于Athena 客户端,您可以将ALTER TABLE mytable ADD PARTITION ...语句生成为字符串,然后将其发送以执行。这是Medium上的一篇文章,可以帮助您入门。

    您也可以使用Glue 客户端batch_create_partition使用or方法执行相同的create_partition操作,但这需要与 Athena 客户端不同的输入

2019-07-03 更新

如果您的数据具有类似的结构

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 数据目录中看到指向正确目标的分区。

于 2019-07-01T14:24:59.767 回答
0

我也遇到过同样的情况。

我为 S3 存储桶手动创建了 Glue 数据目录表。该目录有一些未分配为任何分区键的子目录。通过目录表,Athena 查询甚至处理子目录中的所有文件。但是胶水作业create_dynamic_frame.from_catalog没有。添加additional_options = {"recurse": True}from_catalog,胶水作业在子目录中查找文件。

在我的情况下,目录表具有分区属性"storedAsSubDirectories" = "false",因为当我使用 Glue 控制台或 Athena DDL 查询创建目录表时会自动分配该属性,并且我无法触摸控制台上的值。尽管属性它与附加选项一起使用recurse=True。我怀疑该属性storedAsSubDirectories在这个词的含义中不起作用。

正如@3nochroot 所说,即使在今天,官方文件似乎也没有说明。

于 2020-08-27T07:56:50.313 回答