有这么多帐户,您可能出于多种原因不想使用account_id 作为分区键。我认为你在限制方面很好,the partition limit per table is 1M,但这并不意味着这是一个好主意。
不过,您可以通过对部分帐户 ID 进行分区来显着减少扫描的数据量。如果您的账户 ID 是均匀分布的(如 AWS 账户 ID),您可以按前缀进行分区。如果您的帐户 ID 是第一位数字分区,则每个查询将扫描的数据量减少 90%,两位数减少 99% - 同时仍将分区数量保持在非常合理的水平。
不幸的是,我也不知道如何使用 Glue 来做到这一点。在进行 ETL 时,我发现 Glue 通常非常无用。根据我的经验,即使是简单的事情也很难。使用 Athena 的 CTAS 功能与一些简单的 S3 操作相结合,将 CTAS 操作生成的数据作为分区添加到现有表中,我取得了更大的成功。
如果您想出一种提取帐户 ID 的方法,您还可以尝试为每个帐户使用单独的表,you can have 100K tables in a database。它与表中的分区没有太大区别,但可能更快,具体取决于 Athena 如何确定要查询的分区。
不要太担心 128 MB 文件大小的经验法则。拥有大量小文件确实比拥有少量大文件更糟糕 - 但扫描大量数据以过滤掉一小部分对性能和成本非常不利,这也是事实。 Athena 可以在一秒钟内提供结果,即使是对数百个只有几 KB 大小的文件的查询也是如此。我会担心确保 Athena 先读取正确的数据,然后再考虑理想的文件大小。
如果您告诉我更多关于每个帐户的数据量和帐户的预期寿命,我可以就目标提供更详细的建议。
更新:鉴于 Firehose 不允许您更改输入数据的目录结构,并且 Glue 通常非常糟糕,以及您在评论中提供的附加上下文,我会这样做像这样:
创建一个 Athena 表,其中包含数据中所有属性的列,并将日期作为分区键。这是您的输入表,只会针对此表运行 ETL 查询。不用担心输入数据有单独的年月日目录,你只需要一个分区键。将这些作为单独的分区键只会使事情复杂化,并且拥有一个意味着它可以是 DATE 类型,而不是每次你想约会时都必须组合成一个日期的三个单独的 STRING 列计算。
使用相同的列创建另一个 Athena 表,但按 account_id_prefix 和日期或月份进行分区。这将是您对其运行查询的表。 account_id_prefix 将是您帐户 ID 中的一两个字符——您必须测试最有效的方法。您还必须决定是按日期还是更长的时间跨度进行分区。日期将使 ETL 更容易、更便宜,但更长的时间跨度会产生更少和更大的文件,这可以提高查询效率(但可能更昂贵)。
-
创建执行以下操作的 Step Functions 状态机(在 Lambda 函数中):
- 向输入表添加新分区。如果您安排您的状态机每天运行一次,它只需添加与当前日期对应的分区即可。使用 Glue
CreatePartition API 调用来创建分区(不幸的是,这需要大量信息才能工作,但是您可以运行 GetTable 调用来获取它。例如,将 ["2019-04-29"] 用作 Values 和 @987654337 @ as StorageDescriptor.Location。这相当于运行 ALTER TABLE some_table ADD PARTITION (date = '2019-04-29) LOCATION 's3://some-bucket/firehose/year=2019/month=04/day=29' - 但通过 Glue 执行此操作比在 Athena 中运行查询更快,更适合 Lambda。
- 在输入表上启动CTAS query,并使用当前日期的过滤器,按第一个字符或帐户 ID 和当前日期进行分区。使用低于查询表位置的 CTAS 输出位置。为 CTAS 操作创建的表生成一个随机名称,该表将在后面的步骤中删除。使用 Parquet 作为格式。
- 查看 Poll for Job Status 示例状态机,了解如何等待 CTAS 操作完成。
- CTAS 操作完成后,列出使用 Glue
GetPartitions 创建的临时表中创建的分区,并使用 BatchCreatePartitions 在查询表中创建相同的分区。
- 最后删除属于您删除的查询表分区的所有文件,并删除CTAS操作创建的临时表。
如果您决定对比 date 更长的时间进行分区,您仍然可以使用上述过程,但您还需要删除查询表中的分区和 S3 上的相应数据,因为每次更新都会替换现有数据(例如按月分区,我建议您尝试,每天您都会为整个月创建新文件,这意味着需要删除旧文件)。如果您想每天多次更新您的查询表,那将是相同的。
这看起来很多,看起来就像 Glue Crawlers 和 Glue ETL 所做的那样 - 但根据我的经验,它们并没有那么容易。
在您的情况下,数据是使用 Glue Crawlers 理解的 Hive 样式分区进行分区的,但在许多情况下,您没有获得 Hive 样式分区,而只是 Y/M/D(我实际上并不知道 Firehose 可以以这种方式传递数据,我以为它只做 Y/M/D)。 Glue Crawler 每次运行时也会做很多额外的工作,因为它无法知道数据添加到了哪里,但是你知道从昨天开始添加的唯一分区是昨天的分区,因此减少了爬取一步到位。
Glue ETL 也使事情变得非常困难,与 Lambda 和 Step Functions 相比,它是一项昂贵的服务。您要做的就是将原始数据从 JSON 转换为 Parquet 并重新分区。据我所知,使用比 Athena CTAS 查询更少的代码是不可能的。即使您可以使用 Glue ETL 以更少的代码进行转换操作,您仍然需要编写大量代码来替换目标表中的分区 - 因为这是 Glue ETL 和 Spark 根本不支持的。
Athena CTAS 并不是真的要做 ETL,我认为我上面概述的方法比它应该的要复杂得多,但我相信它比尝试做同样的事情要简单(即根据另一个表中的数据不断更新和可能替换一个表中的分区,而不是每次都重建整个表。
通过此 ETL 过程,您获得的好处是,您的摄取不必担心分区超过时间,但您仍然可以获得针对查询进行优化的表。