未实施 Presto v.s Spark SQL

NOT IN implementation of Presto v.s Spark SQL

我得到了一个非常简单的查询,它显示了当在同一硬件中使用 Spark SQL 和 Presto(3 小时 v.s 3 分钟)运行 时的显着性能差异。

SELECT field 
FROM test1 
WHERE field NOT IN (SELECT field FROM test2)

在查询计划的一些研究之后,我发现原因是 Spark SQL 如何处理 NOT IN 谓词子查询。 为了正确处理 NOT IN 的 NULL,Spark SQL 将 NOT IN 谓词翻译为 Left AntiJoin( (test1=test2) OR isNULL(test1=test2)).

Spark SQL 引入 OR isNULL(test1=test2) 以确保 NOT IN.

的正确语义

但是,Left AntiJoin 连接谓词的 OR 导致 Left AntiJoin 唯一可行的物理连接策略是 BroadcastNestedLoopJoin。对于当前阶段,我可以将 NOT IN 重写为 NOT EXISTS 来解决这个问题。在 NOT EXISTS 的查询计划中,我可以看到连接谓词是 Left AntiJoin(test1=test2),这会导致更好的 NOT EXISTS 物理连接运算符(5 分钟完成)。

到目前为止,我很幸运,因为我的数据集目前没有任何 NULL 属性,但将来可能会有,NOT IN 的语义才是我真正想要的。

所以我检查了 Presto 的查询计划,它并没有真正提供 Left AntiJoin 但它使用 SemiJoinFilterPredicate = not (expr)。 Presto的查询计划不像Spark那样提供太多信息。

所以我的问题更像是:

我可以假设 Presto 有更好的物理连接运算符来处理 NOT IN 操作吗?不像SparkSQL,它不依赖重写连接谓词isnull(op1 = op2)来保证NOT IN在逻辑计划层面的正确语义。

我实际上是在 Presto 中实现 NULL 半连接(IN 谓词)处理的人。

Presto 除了散列分区之外还使用 "replicate nulls and any row" 复制模式¹,这允许它在 IN 的任一侧存在 NULL 的情况下正确处理 IN =],而不回退到广播,或使执行单线程或单节点。运行时性能成本实际上与 NULL 值根本不存在时相同。

如果您想了解有关 Presto 内部结构的更多信息,请加入 Presto Community Slack 上的 #dev 频道。

¹) 准确地说,半连接是散列分区或广播,具体取决于基于成本的决策或配置。