PySpark内连接中连接键isnotnull过滤器重复出现的原因
回答
这种双重过滤的现象是Spark为兼顾性能优化和语义正确性的设计结果,核心原因分为两点:
1. 下推过滤器是性能优化手段,但存在局限性
Spark会将isnotnull(a)这类过滤逻辑下推到数据源(比如这里的Parquet文件),目的是让数据源在读取数据时就提前过滤掉连接键为null的行,减少磁盘IO和后续需要处理的数据量,这是常见的查询优化手段。
但下推过滤无法保证100%生效:
- 部分老旧数据源、自定义数据源可能不支持
IsNotNull这类过滤器的下推; - 即使数据源支持,也可能因为数据格式异常、数据源端bug等原因,导致下推过滤没有完全清理掉null值记录。
2. 显式Filter是Inner Join语义的最后保障
Inner Join的语义明确规定:连接键为null的记录无法与任何行(包括其他null值的行)匹配,这类记录不应该出现在最终结果中。
Spark为严格遵守这个语义,会在数据从数据源读取完成后,额外执行一次isnotnull过滤。这一步是兜底校验,确保即使下推过滤失效,也能把不符合要求的null值记录拦截在Join操作之前,避免产生错误的查询结果。
从你的物理执行计划也能清晰看到这个逻辑:
PushedFilters: [IsNotNull(a)]是Spark发起的数据源端提前过滤请求;- FileScan后的
Filter isnotnull(a#23L)/Filter isnotnull(a#27L)是内存中的二次校验。
这种重复过滤的性能开销几乎可以忽略(因为下推已经过滤了绝大多数无效数据),但能有效避免因数据源端异常导致的结果错误,是Spark严谨性的体现。
内容的提问来源于stack exchange,提问作者figs_and_nuts
相关产品推荐
相关产品推荐

