web-dev-qa-db-ja.com

Sparkシリアライゼーションを理解する

In Sparkどのオブジェクトがドライバーでインスタンス化され、どのオブジェクトがexecutorでインスタンス化されるかをどのようにして知ることができますか。

22
KrazyGautam

オブジェクトをシリアル化するとは、その状態をバイトストリームに変換して、バイトストリームをオブジェクトのコピーに戻すことができるようにすることです。クラスまたはそのスーパークラスのいずれかがJava.io.SerializableインターフェースまたはそのサブインターフェースJava.io.Externalizableを実装している場合、Javaオブジェクトはシリアル化可能です。

クラスがシリアル化されることはありません。クラスのオブジェクトのみがシリアル化されます。オブジェクトをネットワーク上で永続化または送信する必要がある場合は、オブジェクトのシリアル化が必要です。

Class Component            Serialization
instance variable           yes
Static instance variable    no
methods                     no
Static methods              no
Static inner class          no
local variables             no

サンプルSpark=コードを取り、さまざまなシナリオを見てみましょう

public class SparkSample {

      public int instanceVariable                =10 ;
      public static int staticInstanceVariable   =20 ;

      public int run(){

         int localVariable                       =30;

         // create Spark conf
         final SparkConf sparkConf = new SparkConf().setAppName(config.get(JOB_NAME).set("spark.serializer", "org.Apache.spark.serializer.KryoSerializer");

         // create spark context 
         final JavaSparkContext sparkContext = new JavaSparkContext(sparkConf);

        // read DATA 
        JavaRDD<String> lines = spark.read().textFile(args[0]).javaRDD(); 


        // Anonymous class used for lambda implementation
        JavaRDD<String> words = lines.flatMap(new FlatMapFunction<String, String>() {
                @Override
                public Iterator<String> call(String s) {
                // How will the listed varibles be accessed in RDD across driver and Executors 
                System.out.println("Output :" + instanceVariable + " " + staticInstanceVariable + " " + localVariable);
                return Arrays.asList(SPACE.split(s)).iterator();
        });

        // SAVE OUTPUT
        words.saveAsTextFile(OUTPUT_PATH));

      }

       // Inner Static class for the funactional interface which can replace the lambda implementation above 
       public static class MapClass extends FlatMapFunction<String, String>() {
                @Override
                public Iterator<String> call(String s) {
                System.out.println("Output :" + instanceVariable + " " + staticInstanceVariable + " " + localVariable);
                return Arrays.asList(SPACE.split(s)).iterator();
        }); 

        public static void main(String[] args) throws Exception {
            JavaWordCount count = new JavaWordCount();
            count.run();
        }
}

内部クラスオブジェクト内の外部クラスからのインスタンス変数のアクセシビリティとシリアル化可能性

 Inner class           |   Instance Variable (Outer class)   | Static Instance Variable (Outer class)      |  Local Variable (Outer class)

     Anonymous class   |     Accessible And Serialized       | Accessible yet not Serialized                   |  Accessible And Serialized 

    Inner Static class | Not Accessible                      | Accessible yet not Serialized                   | Not Accessible 

理解しながらの経験則Spark job is:

  1. RDD内に記述されたすべてのラムダ関数はドライバーでインスタンス化され、オブジェクトはシリアル化されてエグゼキューターに送信されます

  2. 外部クラスのインスタンス変数が内部クラス内でアクセスされる場合、コンパイラーはそれらにアクセスするために異なるロジックを適用します。したがって、外部クラスはシリアル化されるか、アクセスするかによって異なります。

  3. Javaに関しては、全体の議論は外部クラスと内部クラスについてであり、外部クラスの参照と変数へのアクセスがシリアル化の問題にどのようにつながるかについてです。

さまざまなシナリオ:

Anonymousクラス内でアクセスされる外部クラス変数変数:


インスタンス変数(外部クラス)

コンパイラはデフォルトで、コンストラクタをバイトコードに挿入します

外部クラスオブジェクトを参照する匿名クラス。

外側のクラスオブジェクトは、インスタンス変数にアクセスするために使用されます

匿名クラス(){

 final Outer-class reference;

 Anonymous-class( Outer-class outer-reference){

reference = outer-reference;

}

}

外部クラスはシリアル化され、内部匿名クラスのシリアル化されたオブジェクトと共に送信されます


静的インスタンス変数(外部クラス)

静的変数はシリアル化されないため、外部クラスオブジェクトは引き続き匿名クラスコンストラクターに挿入されます。

静的変数の値は、クラス状態から取得されます

そのexecutorに存在します。


ローカル変数(外部クラス)

コンパイラはデフォルトで、コンストラクタをバイトコードに挿入します

外部クラスオブジェクトとローカル変数の参照を参照する匿名クラス。

外側のクラスオブジェクトは、インスタンス変数にアクセスするために使用されます

匿名クラス(){

 final Outer-class reference;

final Local-variable localRefrence ;

 Anonymous-class( Outer-class outer-reference, Local-variable localRefrence){

reference = outer-reference;

this.localRefrence = localRefrence;

}

}

外部クラスはシリアル化され、ローカル変数オブジェクトも

シリアル化され、内部の匿名クラスのシリアル化されたオブジェクトと共に送信されます

ローカル変数が匿名クラス内のインスタンスメンバーになると、シリアル化する必要があります。外部クラスの観点からは、ローカル変数をシリアル化することはできません

----------

静的内部クラスでアクセスされる外部クラス変数

インスタンス変数(外部クラス)

アクセスできません


ローカル変数(外部クラス)

アクセスできません


静的インスタンス変数(外部クラス)

静的変数はシリアル化されないため、外部クラスオブジェクトはシリアル化されません。

静的変数の値は、クラス状態から取得されます

そのexecutorに存在します。

外部クラスはシリアル化されず、シリアル化された静的内部クラスとともに送信されます


考慮すべき点:

  1. Javaシリアル化ルールに従って、シリアル化する必要のあるクラスオブジェクトを選択します。

  2. Javap -p -c "abc.class"を使用してバイトコードをアンラップし、コンパイラが生成したコードを確認します。

  3. 外部クラスの内部クラス内でアクセスしようとしているものに応じて、コンパイラは異なるバイトコードを生成します。

  4. ドライバーでのみアクセスされるシリアル化を実装するクラスを作成する必要はありません。

  5. RDD内で使用される匿名/静的クラス(すべてのラムダ関数は匿名クラスです)は、ドライバーでインスタンス化されます。

  6. RDD内で使用されるクラス/変数は、ドライバーでインスタンス化され、エグゼキューターに送信されます。

  7. Transientと宣言されたインスタンス変数は、ドライバーでシリアル化されません。

    1. デフォルトでは、匿名クラスにより、外部クラスをシリアル化できるようになります。
    2. ローカル変数/オブジェクトはシリアル化可能である必要はありません。
    3. 匿名クラス内でローカル変数が使用される場合のみ、シリアル化が必要です
    4. Pair、mapToPair関数のcall()メソッド内にシングルトンを作成できます。したがって、ドライバーで初期化されないようにします。
    5. 静的変数はシリアル化されないため、ドライバーからエグゼキューターに送信されることはありません
  8. executorでのみサービスを実行する必要がある場合は、ラムダ関数内の静的フィールドにするか、transientおよびsingeltonにし、null条件をチェックしてインスタンス化する
38
KrazyGautam

このような非常によく説明された非常によく書かれたブログがたくさんあります: spark serialization challenges

しかし、要するに、次のように結論付けることができます(一般的なJVMではなく、Sparkのみ)。

  1. jVMにより、オブジェクトのみをシリアル化できます(関数はオブジェクトです)
  2. オブジェクトをシリアル化する必要がある場合は、親オブジェクトもシリアル化する必要があります
  3. any Spark操作(map、flatMap、filter、foreachPartition、mapPartitionなど)。内側の部分に外側の部分オブジェクトへの参照がある場合、そのオブジェクトはシリアル化する必要があります。外側の部分オブジェクトはドライバーではなく、エグゼキューターでシリアル化ポリシーは私のポイント#2を参照します。
  4. Scala object(aka、Scala singleton)への参照はシリアル化されません(mapPartitionおよびforeachPartitionのみ、UDFは常にserdeを取得しますエグゼキューターは、エグゼキューターJVM上に存在するシングルトンであるため、エグゼキューターはローカルJVMのオブジェクトを直接参照します。つまり、エグゼキューターからローカルobject上のドライバーの変異は見られません。
2
linehrr