使用聚合函数时,减少Athena扫描的数据量

问题描述 投票:2回答:2

以下查询扫描100 MB的数据。

select * from table where column1 = 'val' and partition_id = '20190309';

但是,以下查询扫描15 GB的数据(有超过90个分区)

select * from table where column1 = 'val' and partition_id in (select max(partition_id) from table);

如何优化第二个查询以扫描与第一个相同数量的数据?

sql amazon-athena presto
2个回答
6
投票

这里有两个问题。 select max(partition_id) from table上方的标量子查询的效率和@PiotrFindeisen指出的动态过滤的效率。

第一个问题是对Hive表的分区键的查询比它们出现的要复杂得多。大多数人会认为,如果你想要一个分区键的最大值,你可以简单地对分区键执行查询,但这不起作用,因为Hive允许分区为空(并且它还允许非空文件不包含任何行)。具体来说,select max(partition_id) from table上方的标量子查询要求Presto找到包含至少一行的最大分区。理想的解决方案是在Hive中拥有完美的统计数据,但是缺少引擎需要为打开分区文件的hive定制逻辑,直到找到非空的分区。

如果您确定您的仓库不包含空分区(或者您可以确定其含义),则可以使用隐藏的$partitions表替换标量子查询“

select * 
from table 
where column1 = 'val' and 
    partition_id = (select max(partition_id) from "table$partitions");

第二个问题是@PiotrFindeisen指出的问题,并且与计划执行查询的方式有关。大多数人会查看上面的查询,看看引擎应该在规划期间明确地计算select max(partition_id) from "table$partitions"的值,将其内联到计划中,然后继续优化。不幸的是,这是一个非常复杂的决策,因此引擎只是简单地将其建模为广播连接,其中执行的一部分计算出该值,并将值传播给其余的工作者。问题是执行的其余部分无法将此新信息添加到现有处理中,因此它只扫描所有数据,然后筛选出您尝试跳过的值。有一个项目正在进行中添加这个dynamic filtering,但还没有完成。

这意味着您今天可以做的最好的事情是运行两个单独的查询:一个用于获取max partition_id,另一个用于内联值。

顺便说一句,在Presto 0.199中添加了隐藏的“$ partitions”表,我们修复了0.201中的一些小错误。我不确定雅典娜的版本基于哪个版本,但我相信它已经过时了(我写这个答案的当前版本是309


1
投票

编辑:Presto删除了他们的__internal_partitions__中的0.193 release表,因此我建议不要在任何生产系统中使用下面Slow aggregation queries for partition keys部分中定义的解决方案,因为Athena'透明地'更新presto版本。我最后只是使用天真的SELECT max(partition_date) ...查询,但也使用了Lack of Dynamic Filtering部分中概述的相同的回顾技巧。它比使用__internal_partitions__表慢约3倍,但至少当Athena决定更新其presto版本时它不会破坏。

----- Original Post -----

所以我想出了一个相当hacky的方法来实现这个基于日期的大型数据集分区,当你只需要回顾几个分区的数据以获得最大匹配时,不过请注意我不是100%确定information_schema.__internal_partitions__表的使用有多脆弱。

正如上面提到的@Dain,实际上有两个问题。第一个是max(partition_date)查询聚合的速度有多慢,第二个是Presto缺乏对动态过滤的支持。

分区键的慢速聚合查询

为了解决第一个问题,我正在使用information_schema.__internal_partitions__表,它允许我在表的分区上快速聚合而不扫描文件中的数据。 (请注意,以下查询中的partition_valuepartition_keypartition_number都是__internal_partitions__表的列名,与表的列无关)

如果您的表只有一个分区键,则可以执行以下操作:

SELECT max(partition_value) FROM information_schema.__internal_partitions__
WHERE table_schema = 'DATABASE_NAME' AND table_name = 'TABLE_NAME'

但是如果你有多个分区键,你需要更像这样的东西:

SELECT max(partition_date) as latest_partition_date from (
  SELECT max(case when partition_key = 'partition_date' then partition_value end) as partition_date, max(case when partition_key = 'another_partition_key' then partition_value end) as another_partition_key
  FROM information_schema.__internal_partitions__
  WHERE table_schema = 'DATABASE_NAME' AND table_name = 'TABLE_NAME'
  GROUP BY partition_number
)
WHERE
  -- ... Filter down by values for e.g. another_partition_key
)

这些查询应该运行得相当快(我的运行大约1-2秒)而不扫描文件中的实际数据,但同样,我不确定是否有使用此方法的任何问题。

缺乏动态过滤

我能够减轻第二个问题对我的特定用例的最坏影响,因为我希望在当前日期之后的有限时间内总是存在分区(例如,我可以保证任何数据生成或分区 - 加载问题将在3天内得到纠正)。事实证明,Athena在使用presto的datetime functions时确实进行了一些预处理,因此动态过滤与使用子查询的问题不同。

因此,您可以更改查询以限制使用日期时间函数查看实际最大值的距离,以便限制扫描的数据量。

SELECT * FROM "DATABASE_NAME"."TABLE_NAME"
WHERE partition_date >= cast(date '2019-06-25' - interval '3' day as varchar) -- Will only scan partitions from 3 days before '2019-06-25'
AND partition_date = (
  -- Insert the partition aggregation query from above here
)
© www.soinside.com 2019 - 2024. All rights reserved.