100列と1,000万レコードの入力テーブル(I)があります。 50列の出力テーブル(O)を取得したいのですが、これらの列はIの列から派生しています。つまり、Iの列をOの50列にマップする50個の関数がありますo1 = f(i1)、o2 = f(i2、i3)...、o50 = f(i50、i60、i70)。
spark sqlでは、2つの方法でこれを行うことができます。
上記の2つのうちどちらがより効率的であるか(より高い分散および並列処理)、なぜ、またはそれらが同等に高速/パフォーマンスであるかどうかを知りたいです。入力テーブルI全体を処理し、まったく新しい出力テーブルO、つまりバルクデータ処理を作成していること。
Catalystオプティマイザー についてこのすべてを書くつもりでしたが、Jacek Laskowskiが彼の本Mastering Apacheで言っていることに注意する方が簡単ですSpark 2:
"UDFはSparkのブラックボックスであるため、独自のカスタムUDF関数の使用に戻る前に、可能な限り、データセット演算子で高レベルの標準列ベース関数を使用してくださいそのため、それらを最適化しようとさえしません。 "
Jacekは、Spark開発チームの誰かからのコメントにも注目しています:
「UDFバイトコードを分析してそれが何をしているのかを推測できる単純なケースがありますが、一般的に行うのはかなり困難です。」
これが、Spark UDFを最初のオプションにするべきではない理由です。
同じ感情がこのCloudera post にも反映されており、作成者は「... ApacheSparkの組み込みSQLクエリ関数を使用すると頻繁に最高のパフォーマンスを実現し、UDFの導入を回避できる場合は常に最初に検討する必要があります。 "
ただし、作者は、Sparkがよりスマートになるにつれて、これは将来変更される可能性があることにも正しく注意しています。その間、Chris Freglyの で説明されているように、Expression.genCode
を使用できます。トーク 、Catalystオプティマイザーとの緊密な結合を気にしない場合。
ユーザー定義関数またはカスタム関数は、SQLクエリで使用できるように関連付けられたエイリアスを使用してSpark SQLでUDFとして定義および登録できます。
UDFはApacheよりもパフォーマンスに大きな影響を与えますSpark SQL( SparkSQLのCatalystOptimizer )
Sparkには定義済みのルールがないため、開発者はデューデリジェンスを使用できます。
Python UDFはUDFを使用しません。 PythonインタープリターとJVMの間で繰り返されるシリアル化、逆シリアル化、およびデータ移動のコストを補償することは不可能です。Python UDFは、実行者のJVMとJVMの間でデータがシリアル化される結果になります。 Python UDFロジックを実行するインタープリター–これにより、JavaまたはScalaのUDF実装と比較して、パフォーマンスが大幅に低下します。
Java、Scala UDF実装はエグゼキュータJVMから直接アクセスできます。だからJava、ScalaUDFのパフォーマンスはPython UDFよりも優れています
Spark SQL関数はJVMで直接動作し、CatalystとTungstenの両方で最適化します。つまり、これらは実行プランで最適化でき、ほとんどの場合、codgenやその他のタングステンの最適化の恩恵を受けることができます。さらに、Spark SQLはCatalystクエリオプティマイザーと連携するため、これらは「ネイティブ」表現でデータを操作できます。その機能はリリースごとに拡張され、多くの場合、=に劇的なパフォーマンスの向上をもたらすことができます。 Spark SQLクエリ;
結論:UDF実装コードはCatalystによって十分に理解されていない可能性があるため、Apache Sparkの組み込みSQLクエリ関数を使用すると、多くの場合、最高のパフォーマンスが得られます。 UDFの導入を回避できる場合は常に最初に検討するアプローチです。
単純なケースでは、完全なエンコードとデコードを必要としないため、UDFを使用する方がはるかに適しています。 UDFは、必要なフィールドにのみアクセスして、結果をエンコードできます。
Spark 2.0以降、サポートも大幅に向上し、実行プランである程度最適化できます。
完全なRow
のマッピングと標準UDFの適用の両方が、すべてのSpark SQL最適化の恩恵を受けることはできず、データ分散と並列化の点で違いはありません。
Sparkには 組み込み関数 _new dataframe
_の列を使用して_parent dataframe
_があります。
これらの関数は、udf
関数を使用するかrow
変換を使用するという2つの提案された選択肢よりもパフォーマンスが高くなります。
データの最適な分散を考慮して、ファイナルテーブルの50個の必須列にrow
変換関数を作成するのは非常に困難です。
関数(o1 = f(i1) , o2 = f(i2, i3) ..., o50 = f(i50, i60, i70)
)を 組み込み関数 またはそれらの組み合わせで置き換えることができない場合は、udf
関数をudf関数として使用することをお勧めします。データをシリアル化および逆シリアル化する必要があります。