下面的查询扫描 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);
如何优化第二个查询以扫描与第一个相同数量的数据?
最佳答案
这里有两个问题。 select max(partition_id) from table
上面标量子查询的效率,以及@PiotrFindeisen 指出的关于动态过滤的效率。
第一个问题是对 Hive 表的分区键的查询比它们看起来复杂得多。大多数人会认为,如果您想要分区键的最大值,您可以简单地对分区键执行查询,但这不起作用,因为 Hive 允许分区为空(并且它还允许非空文件不包含行)。具体来说,select max(partition_id) from table
上面的标量子查询需要 Trino (formerly PrestoSQL) 找到至少包含一行的最大分区。理想的解决方案是在 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 ,但尚未完成。这意味着您今天可以做的最好的事情是运行两个单独的查询:一个获取最大 partition_id,另一个获取内联值。
顺便说一句,隐藏的“$partitions”表是在 Presto 0.199 中添加的,我们修复了 0.201 中的一些小错误。我不确定 Athena 基于哪个版本,但我相信它已经过时了(我写这个答案时的当前版本是 309 。
关于sql - 使用聚合函数时减少 Athena 扫描的数据量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55873006/