Spark解決SQL和RDDjoin結果不一致問題(工作實錄)

夢裡繁花發表於2021-10-21

問題描述:DataFrame的join結果不正確,dataframeA(6000無重複條資料) join dataframeB(220條無重複資料,由dataframeA轉化而來,key值均源於dataframeA) 只有200條資料,丟了20條

問題驗證:

1,查詢丟的20條資料,均無異常,不存在Null,資料不存在空格

2,重新執行演算法,丟18條資料,證明丟資料存在一定隨機性

3,簡化問題到最簡模式,程式碼如下:

    val xxx1= phySiteEvaluationPhySiteKey.select("physitekey").distinct()
    val xxx2= physitefinal.select("physitekey").distinct()
    val xxx3 = xxx1.join(xxx2, Seq("physitekey"))

    val rdd1=xxx1.rdd.map(r=>r.getAs[String]("physitekey")).map(r=>(r,r))
    val rdd2 =xxx2.rdd.map(r=>r.getAs[String]("physitekey")).map(r=>(r,r))
    val rdd3=rdd1.join(rdd2)

    log.info(s"rdd3=${rdd3.count()}")
    log.info(s"xxx3==${xxx3.count()}")

xxx3和rdd3的結果居然不相等!!違背了spark常識

問題分析:

1,據spark原理可知,DataFrame的底層實現就是RDD,具體實現在Catalyst包類,需要DataFrame=>未解析的邏輯執行計劃=>解析邏輯計劃=>優化邏輯執行計劃=>物理執行計劃=>RDD執行

也就是說xxx3的執行計劃生成出的RDD執行方案與RDD3結果不一致,因此在這裡我列印了xxx3的執行計劃,期望有所發現

    xxx1.join(xxx2, Seq("physitekey")).explain()

執行計劃長達1000多行,涉及內部實現因專案保密需要無法展示。

2,執行計劃超長是因為phySiteEvaluationPhySiteKey、physitefinal均為迭代計算結果,不是直接來源於輸入表

3,依據執行計劃,我猜測Spark在邏輯計劃優化的時候出錯,導致結果不符合預期

4,驗證方案:為xxx1、xxx2的取值加上checkpoint,斬斷血緣依賴,重新檢視執行計劃是否符合預期

    val xxx1= phySiteEvaluationPhySiteKey.select("physitekey").distinct().checkpoint()
    val xxx2= physitefinal.select("physitekey").distinct().checkpoint()
    xxx1.join(xxx2, Seq("physitekey")).explain()
    val xxx3 = xxx1.join(xxx2, Seq("physitekey"))

    val rdd1=xxx1.rdd.map(r=>r.getAs[String]("physitekey")).map(r=>(r,r))
    val rdd2 =xxx2.rdd.map(r=>r.getAs[String]("physitekey")).map(r=>(r,r))
    val rdd3=rdd1.join(rdd2)

    log.info(s"rdd3=${rdd3.count()}")
    log.info(s"xxx3==${xxx3.count()}")

結果執行計劃如下:

== Physical Plan ==
*Project [physitekey#1648]
+- *SortMergeJoin [physitekey#1648], [physitekey#43875], Inner
   :- *Sort [physitekey#1648 ASC NULLS FIRST], false, 0
   :  +- Exchange(coordinator id: 1135069612) hashpartitioning(physitekey#1648, 200), coordinator[target post-shuffle partition size: 67108864]
   :     +- *Filter isnotnull(physitekey#1648)
   :        +- Scan ExistingRDD[physitekey#1648]
   +- *Sort [physitekey#43875 ASC NULLS FIRST], false, 0
      +- Exchange(coordinator id: 1135069612) hashpartitioning(physitekey#43875, 200), coordinator[target post-shuffle partition size: 67108864]
         +- *Filter isnotnull(physitekey#43875)
            +- Scan ExistingRDD[physitekey#43875]

沒有問題,RDD3與XXX3結果相等,正確了。

確認問題出在Spark中DataFrame在持有超長血緣關係時轉化為RDD執行出錯,具體錯誤有機會下次分析,應當是僅在一定特殊情況下才會暴露的BUG

5、問題反思

開源元件也是可能存在BUG的,應當在使用時儘量使用其最常見的用法,列如在本問題中,如果迭代計算之後及時斬斷血緣依賴,就不會出現問題

相關文章