AWS Athena查询分区

问题描述 投票:0回答:1

我正在尝试使用AWS Athena为现有平台提供分析。目前流程如下:

  1. 数据作为JSON事件被泵入Kinesis Firehose。
  2. Firehose使用AWS Glue中的表将数据转换为镶木地板,并且每15分钟或当流达到128 MB(最大支持值)时写入S3。
  3. 当数据写入S3时,它将使用路径/year=!{timestamp:yyyy}/month=!{timestamp:MM}/day=!{timestamp:dd}/...进行分区
  4. AWS Glue爬网程序每24小时更新一个包含最新分区数据的表,并使其可用于查询。

基本流程有效。但是,这有几个问题......

第一个(也是最重要的)是这个数据是多租户应用程序的一部分。每个活动都有一个名为account_id的房产。将要发布的每个查询都将由特定帐户发出,我不希望扫描每个查询的所有帐户数据。我需要找到一种可扩展的方式来查询相关数据。我确实试图让我们Kinesis提取account_id并将其用作分区。但是,目前不支持这种情况,并且对于> 10,000个帐户,AWS 20k分区限制很快就会成为问题。

第二个问题是文件大小! AWS建议文件不要小于128 MB,因为这会对查询时间产生不利影响,因为执行引擎可能会花费额外的时间来打开Amazon S3文件。考虑到Firehose的性质,我每个文件的最大大小只能达到128 MB。

amazon-web-services amazon-s3 amazon-athena amazon-kinesis-firehose
1个回答
1
投票

有了这么多帐户,您可能不希望将account_id用作分区键,原因有很多。我认为你是好的限制,the partition limit per table is 1M,但这并不意味着它是一个好主意。

但是,您可以通过对帐户ID的某些部分进行分区来显着减少扫描的数据量。如果您的帐户ID是统一分布的(例如AWS账户ID),则可以对前缀进行分区。如果您的帐户ID是第一个数字上的数字分区,则每个查询将扫描的数据量减少90%,两个数字减少99% - 同时仍将分区数保持在非常合理的水平。

不幸的是,我不知道如何用胶水做到这一点。我发现在做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更容易和更便宜,但更长的时间跨度将产生更少和更大的文件,这可以使查询更有效(但可能更昂贵)。
  • 创建执行以下操作的步骤函数状态机(在Lambda函数中): 将新分区添加到输入表。如果您将状态机安排为每天运行一次,则只需添加与当前日期对应的分区即可。使用Glue CreatePartition API调用来创建分区(不幸的是,这需要很多信息才能工作,你可以运行GetTable调用来获取它。例如使用["2019-04-29"]作为Values"s3://some-bucket/firehose/year=2019/month=04/day=29"作为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操作创建的临时表。

如果您决定对比某个日期更长的内容进行分区,您仍然可以使用上述过程,但您还需要删除查询表中的分区以及S3上的相应数据,因为每次更新都将替换现有数据(例如,按月分区,我建议你尝试,每天你会创建整个月的新文件,这意味着需要删除旧文件)。如果要每天多次更新查询表,它将是相同的。

这看起来很多,看起来像Glue Crawlers和Glue ETL那样做 - 但根据我的经验,他们并没有这么容易。

在你的情况下,数据使用Hive样式分区进行分区,Glue Crawlers理解,但在许多情况下你不会得到Hive样式分区而只有Y / M / D(我实际上并不知道Firehose可以提供数据方式,我认为它只做了Y / M / D)。 Glue Crawler每次运行时也会做很多额外的工作,因为它无法知道数据的添加位置,但是你知道自昨天以来添加的唯一分区是昨天的分区,因此爬行减少了一步到位的交易。

胶水ETL也使事情变得非常困难,与Lambda和Step Functions相比,它是一项昂贵的服务。您要做的就是将原始数据从JSON转换为Parquet并重新分区。据我所知,使用比Athena CTAS查询更少的代码不可能做到这一点。即使您可以用较少的代码使用Glue ETL进行转换操作,您仍然需要编写大量代码来替换目标表中的分区 - 因为这是Glue ETL和Spark根本不支持的内容。

Athena CTAS并没有真正做ETL,我认为我上面概述的方法要比它应该复杂得多,但我相信它不如尝试做同样的事情复杂(即不断更新)并且可能会根据另一个表中的数据替换表中的分区,而不必每次都重建整个表。

您通过此ETL过程获得的是,您的摄取不必担心分区超过时间,但您仍然可以获得针对查询进行优化的表。

© www.soinside.com 2019 - 2024. All rights reserved.