通过S3创建日期的分区Athena查询

时间:2019-03-08 15:56:23

标签: amazon-s3 amazon-athena aws-glue

我有一个S3存储桶,其中包含约7000万个JSON(约15TB)和一个雅典娜表,可按时间戳和JSON中定义的其他键进行查询。

可以保证JSON中的时间戳或多或少等于JSON的S3createdDate(或至少等于我的查询目的)

我可以通过将createddate添加为“分区”之类的方式来提高查询性能(和成本)吗?我不理解,似乎只有前缀/文件夹才有可能?

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

2 个答案:

答案 0 :(得分:2)

无法让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/'
  ADD 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/'
  ADD PARTITION (created_date = '2019-03-08') LOCATION 's3://some-bucket/data/created_date=2019-03-08/'

为此,我遇到的唯一与此功能相关的雅典娜相关文档是S3 Inventory docs for integrating with Athena

答案 1 :(得分:2)

我开始研究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/'
 ...