Sagen wir, ich habe den folgenden Code:
%Vor%Jetzt führen wir diesen Code in Spark aus:
%Vor% Der obige Code löst org.apache.spark.SparkException: Task not serializable
aus. Ich bin frage nicht, wie man es beheben kann , indem ich Serializable erweitern oder eine Fall-Klasse mache, möchte ich verstehen, warum der Fehler passiert.
Was ich nicht verstehe ist, warum es sich darüber beschwert, dass die Context-Klasse kein Serializable
ist, obwohl es kein Teil des Lambda ist: rdd.map(_ + data(0))
. data
hier ist ein Array von Werten, die serialisiert werden sollten, aber es scheint, dass JVM auch ctx
referenz erfasst, was meines Wissens nach nicht passieren sollte.
Wie ich verstehe, sollte Spark in der Shell Lambda aus dem repl-Kontext löschen. Wenn wir den Baum nach delambdafy
phase drucken, würden wir diese Teile sehen:
Der dekompilierte Lambda-Code, der an den Worker-Knoten gesendet wird, lautet also: x.+(iw.this.data().apply(0))
. Teil iw.this
gehört zur Spark-Shell-Sitzung, also sollte es, wie ich es verstehe, durch ClosureCleaner , da hat nichts mit der Logik zu tun und sollte nicht serialisiert werden. Wie auch immer, Aufruf von iw.this.data()
gibt einen Array[Double]
Wert der data
Variable zurück, die im Konstruktor initialisiert wird:
Nach meinem Verständnis hat ctx
value nichts mit dem Lambda zu tun, es ist kein Abschluss, daher sollte es nicht serialisiert werden. Was vermisse ich oder missverstanden?
Das hat damit zu tun, was Spark für sicher halten kann. Dies ist in einigen Fällen sehr intuitiv, da Spark Reflexionen verwendet und in manchen Fällen einige der Garantien von Scala nicht erkennen kann (kein vollständiger Compiler oder irgendetwas) oder die Tatsache, dass einige Variablen in demselben Objekt irrelevant sind. Aus Sicherheitsgründen wird Spark versuchen, alle referenzierten Objekte zu serialisieren, was in Ihrem Fall iw
einschließt, was nicht serialisierbar ist.
Der Code in ClosureCleaner hat ein gutes Beispiel:
Zum Beispiel ist eine transitive Reinigung im folgenden notwendig Szenario:
%Vor%In diesem Beispiel ist der Bereich "zwei" nicht serialisierbar, da er den Bereich "eins" referenziert, der auf SomethingNotSerializable verweist. Beachten Sie jedoch, dass der Textbereich von "zwei" tatsächlich nicht von SomethingNotSerializable abhängt. Das bedeutet, dass wir den Parent-Zeiger eines geklonten Bereichs "Eins" aufheben und ihn zum Parent des Bereichs "Zwei" machen können, so dass der Bereich "Zwei" nicht mehr auf SomethingNotSerializal transitiv verweist.
Wahrscheinlich ist die einfachste Lösung, eine lokale Variable im selben Gültigkeitsbereich zu erstellen, die den Wert aus Ihrem Objekt extrahiert, so dass kein Verweis mehr auf das einkapselnde Objekt innerhalb des Lambda vorhanden ist:
%Vor%Tags und Links scala serialization jvm apache-spark