没有办法让 Athena 使用诸如 S3 对象元数据之类的东西来进行查询计划。让 Athena 跳过读取对象的唯一方法是组织对象,以便可以设置分区表,然后使用分区键上的过滤器进行查询。
听起来您对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 中以不同的方式引用它......),因为它是一个保留字。
然后你创建一个分区表:
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 来加载表的分区。如果您使用 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/'
最后,当您查询表时,请确保包含 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 上组织对象的方式,则有一个文档记录不充分的功能,即使您无法更改数据对象,也可以创建分区表。您所做的是创建与我上面建议的相同的前缀,但不是将 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
然后您创建一个看起来与上表非常相似的表格,但有一点不同:
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 属性的值。
您可以像添加任何分区表一样添加分区:
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。