按 S3 创建日期分区 Athena 查询

Partition Athena query by S3 created date

我有一个包含约 7000 万 JSONs(约 15TB)的 S3 存储桶和一个 athena table 以按时间戳和 JSON 中定义的一些其他键进行查询。

可以保证 JSON 中的时间戳或多或少等于 JSON 的 S3-createdDate(或至少足以满足我的查询目的)

我可以通过将创建日期添加为 "partition" 之类的东西来以某种方式提高查询性能(和成本)吗?我认为这似乎只适用于 prefixes/folders?

编辑: 我目前通过使用 S3 库存 CSV 来通过 createdDate 进行预过滤然后下载所有 JSONs 并进行其余的过滤来模拟它,但如果可能的话我想完全在雅典娜内部进行

无法让 Athena 使用诸如 S3 对象元数据之类的东西来进行查询规划。让 Athena 跳过读取对象的唯一方法是以一种可以设置分区 table 的方式组织对象,然后使用分区键上的过滤器进行查询。

听起来您对 partitioning in Athena 的工作原理有所了解,我认为您没有使用它是有原因的。但是,为了遇到此问题的其他有类似问题的人的利益,我将首先解释如果您可以更改对象的组织方式可以做什么。我会在最后给出替代建议,您可能想直接跳到那个。

我建议您使用包含部分对象时间戳的前缀来组织 JSON 对象。具体多少取决于您查询数据的方式。你不希望它太细,也不要太粗。使其过于细化会使 Athena 花费更多时间在 S3 上列出文件,使其过于粗糙会使它读取太多文件。如果查询最常见的时间段是一个月,这是一个很好的粒度,如果最常见的时间段是几天,那么一天可能更好。

例如,如果日期是您数据集的最佳粒度,您可以使用如下键组织对象:

s3://some-bucket/data/2019-03-07/object0.json
s3://some-bucket/data/2019-03-07/object1.json
s3://some-bucket/data/2019-03-08/object0.json
s3://some-bucket/data/2019-03-08/object1.json
s3://some-bucket/data/2019-03-08/object2.json

您还可以使用 Hive 样式的分区方案,这是其他工具(如 Glue、Spark 和 Hive)所期望的,因此除非您有理由不这样做,否则它可以避免您将来的悲伤:

s3://some-bucket/data/created_date=2019-03-07/object0.json
s3://some-bucket/data/created_date=2019-03-07/object1.json
s3://some-bucket/data/created_date=2019-03-08/object0.json

这里我取的名字是created_date,不知道给你的数据起什么名字比较好。您可以只使用 date,但请记住始终引用它(并在 DML 和 DDL 中以不同的方式引用它……),因为它是一个保留字。

然后你创建一个分区table:

CREATE TABLE my_data (
  column0 string,
  column1 int
)
PARTITIONED BY (created_date date)
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/'
TBLPROPERTIES ('has_encrypted_data'='false')

然后一些指南会告诉您 运行 MSCK REPAIR TABLE 加载 table 的分区。如果你使用 Hive 风格的分区(即 …/created_date=2019-03-08/…),你可以这样做,但它会花费很长时间,我不推荐它。您可以通过手动添加分区来做得更好,您可以这样做:

ALTER TABLE my_data ADD
  PARTITION (created_date = '2019-03-07') LOCATION 's3://some-bucket/data/created_date=2019-03-07/'
  PARTITION (created_date = '2019-03-08') LOCATION 's3://some-bucket/data/created_date=2019-03-08/'

最后,当您查询 table 时,请确保包含 created_date 列以向 Athena 提供它只读取与查询相关的对象所需的信息:

SELECT COUNT(*)
FROM my_data
WHERE created_date >= DATE '2019-03-07'

当您从 created_date >= DATE '2019-03-07' 更改为 created_date = DATE '2019-03-07'.

时,您可以通过观察扫描数据的差异来验证查询是否更便宜

如果您无法更改对象在 S3 上的组织方式,有一个记录不详的功能,即使您无法更改数据对象,也可以创建分区 table .你所做的是创建与我上面建议的相同的前缀,但不是将 JSON 对象移动到这个结构中,而是在每个分区的前缀中放置一个名为 symlink.txt 的文件:

s3://some-bucket/data/created_date=2019-03-07/symlink.txt
s3://some-bucket/data/created_date=2019-03-08/symlink.txt

在每个 symlink.txt 中,您将要包含在该分区中的文件的完整 S3 URI 放入。例如,在第一个文件中,您可以放置​​:

s3://data-bucket/data/object0.json
s3://data-bucket/data/object1.json

和第二个文件:

s3://data-bucket/data/object2.json
s3://data-bucket/data/object3.json
s3://data-bucket/data/object4.json

然后你创建一个看起来与上面的 table 非常相似的 table,但有一点不同:

CREATE TABLE my_data (
  column0 string,
  column1 int
)
PARTITIONED BY (created_date date)
ROW FORMAT SERDE 'org.openx.data.jsonserde.JsonSerDe' 
STORED AS INPUTFORMAT 'org.apache.hadoop.hive.ql.io.SymlinkTextInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://some-bucket/data/'
TBLPROPERTIES ('has_encrypted_data'='false')

注意 INPUTFORMAT 属性.

的值

您可以像添加任何分区一样添加分区 table:

ALTER TABLE my_data ADD
  PARTITION (created_date = '2019-03-07') LOCATION 's3://some-bucket/data/created_date=2019-03-07/'
  PARTITION (created_date = '2019-03-08') LOCATION 's3://some-bucket/data/created_date=2019-03-08/'

我遇到的关于此功能的唯一 Athena 相关文档是 S3 Inventory docs for integrating with Athena

我开始使用 Theo 的答案并且非常接近(谢谢 Theo 的出色且非常详细的回复),但是根据 documentation 添加多个分区时,您只需要指定 "ADD" 在查询开始附近。

我尝试按照 Theo 的示例在每一行上指定 "ADD",但收到错误消息。不过,它仅在指定一次时有效。以下是我使用的成功格式:

ALTER TABLE db.table_name ADD IF NOT EXISTS
 PARTITION (event_date = '2019-03-01') LOCATION 's3://bucket-name/2019-03-01/'
 PARTITION (event_date = '2019-03-02') LOCATION 's3://bucket-name/2019-03-02/'
 PARTITION (event_date = '2019-03-03') LOCATION 's3://bucket-name/2019-03-03/'
 ...