未实施 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
但它使用 SemiJoin
和 FilterPredicate = 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
频道。
¹) 准确地说,半连接是散列分区或广播,具体取决于基于成本的决策或配置。
我得到了一个非常简单的查询,它显示了当在同一硬件中使用 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
但它使用 SemiJoin
和 FilterPredicate = 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
频道。
¹) 准确地说,半连接是散列分区或广播,具体取决于基于成本的决策或配置。