Detected cartesian product for INNER join on literal column in PySpark

后端 未结 1 1083
無奈伤痛
無奈伤痛 2021-01-13 12:31

The following code raises \"Detected cartesian product for INNER join\" exception:

first_df = spark.createDataFrame([{         


        
相关标签:
1条回答
  • 2021-01-13 12:42

    The problem is, that once you persist your data, second_id is incorporated into the cached table and no longer considered constant. As a result planner can no longer infer that the query should be expressed a Cartesian product, and uses standard SortMergeJoin on hash partitioned second_id.

    It would be trivial to achieve the same outcome, without persistence, using udf

    from pyspark.sql.functions import lit, pandas_udf, PandasUDFType
    
    @pandas_udf('integer', PandasUDFType.SCALAR)                   
    def identity(x):                                                   
        return x    
    
    second_df = second_df.withColumn('second_id', identity(lit(1)))
    
    result_df = first_df.join(second_df,
                             first_df.first_id == second_df.second_id,
                             'inner')
    
    result_df.explain()
    

    == Physical Plan ==
    *(6) SortMergeJoin [cast(first_id#4 as int)], [second_id#129], Inner
    :- *(2) Sort [cast(first_id#4 as int) ASC NULLS FIRST], false, 0
    :  +- Exchange hashpartitioning(cast(first_id#4 as int), 200)
    :     +- *(1) Filter isnotnull(first_id#4)
    :        +- Scan ExistingRDD[first_id#4]
    +- *(5) Sort [second_id#129 ASC NULLS FIRST], false, 0
       +- Exchange hashpartitioning(second_id#129, 200)
          +- *(4) Project [some_value#6, pythonUDF0#154 AS second_id#129]
             +- ArrowEvalPython [identity(1)], [some_value#6, pythonUDF0#154]
                +- *(3) Project [some_value#6]
                   +- *(3) Filter isnotnull(pythonUDF0#153)
                      +- ArrowEvalPython [identity(1)], [some_value#6, pythonUDF0#153]
                         +- Scan ExistingRDD[some_value#6]
    

    However SortMergeJoin is not what you should try to achieve here. With constant key, it would result in an extreme data skew, and likely fail, on anything but toy data.

    Cartesian Product however, as expensive as it is, won't suffer from this issue, and should be preferred here. So it would recommend enabling cross joins or using explicit cross join syntax (spark.sql.crossJoin.enabled for Spark 2.x) and move on.

    A pending question remains how to prevent undesired behavior when data is cached. Unfortunately I don't have an answer ready for that. I fairly sure it is possible to use custom optimizer rules, but this is not something that can be done with Python alone.

    0 讨论(0)
提交回复
热议问题