Apache spark Spark流中的序列化问题
我很困惑Spark是如何处理引擎盖下的数据的。例如,当我运行流作业并应用Apache spark Spark流中的序列化问题,apache-spark,apache-spark-sql,spark-streaming,apache-spark-ml,Apache Spark,Apache Spark Sql,Spark Streaming,Apache Spark Ml,我很困惑Spark是如何处理引擎盖下的数据的。例如,当我运行流作业并应用foreachRDD时,行为取决于变量是从外部范围捕获还是在内部初始化 val sparkConf = new SparkConf() dStream.foreachRDD(rdd => { val spark = SparkSession.builder.config(sparkConf).getOrCreate() ... }) 在这种情况下,我得到一个例外: java.io.NotSeriali
foreachRDD
时,行为取决于变量是从外部范围捕获还是在内部初始化
val sparkConf = new SparkConf()
dStream.foreachRDD(rdd => {
val spark = SparkSession.builder.config(sparkConf).getOrCreate()
...
})
在这种情况下,我得到一个例外:
java.io.NotSerializableException:org.apache.spark.streaming.kafka.DirectKafkaInputDStream$DirectKafkaInputDStreamCheckpointData的对象正在序列化,可能是RDD操作关闭的一部分。这是因为DStream对象是从闭包中引用的。请在此数据流中重写RDD操作以避免此情况。这是为了避免Spark任务因不必要的对象而膨胀
但是如果我把sparkConf
移到里面,一切似乎都很好:
dStream.foreachRDD(rdd => {
val sparkConf = rdd.sparkContext.getConf
val spark = SparkSession.builder.config(sparkConf).getOrCreate()
...
})
这在我看来很奇怪,因为我认为foreachRDD
在驱动程序节点上运行,所以我不希望有任何不同
现在,如果我将SQL会话和配置移到外部foreachRDD
,它会再次正常工作:
val sparkConf = new SparkConf()
val spark = SparkSession.builder.config(sparkConf).getOrCreate()
dStream.foreachRDD(rdd => {
val df = spark.read.json(rdd)
...
})
Spark中的一个文档建议使用以前的版本(在foreachRDD
中创建config和SQL上下文),这在我看来效率较低:如果可以只创建一次,为什么要为每个批创建它们
有人能解释一下引发异常的原因,以及创建SQL上下文的正确方法吗?正如名称所示,foreach rdd在流中运行,foreach rdd为什么要在每个rdd上重新创建spark上下文? 正确的方法是最后一种:
val sparkConf = new SparkConf()
val spark = SparkSession.builder.config(sparkConf).getOrCreate()
dStream.foreachRDD(rdd => {
val df = spark.read.json(rdd)
...
})
val spark=SparkSession.builder.config(sparkConf.getOrCreate()
不创建另一个SparkSession
。只有一个存在。
在
worker
上,只需从job
获取它。在第一种方法中,您试图为每个不正确的分区实例化spark会话对象
正如其他人所回答的,使用第三种方法。但如果您需要使用第一种方法,那么您可以使用以下方法-
val sparkConf = new SparkConf()
dStream.foreachRDD(rdd => {
lazy val spark = SparkSession.builder.config(sparkConf).getOrCreate()
...
})
在这里,延迟评估将有助于避免多次实例化spark会话,从而避免序列化问题
我希望这会有帮助。我认为foreachRDD在驱动程序节点上运行,传递给
foreachRDD
的方法在工作程序上运行,而不是在驱动程序上运行。@YuvalItzchakov我不这么认为,因为foreachRDD
在整个RDD上运行,而不是在该RDD的分区或元素上运行。文档明确指出它在驱动程序节点上运行:你说得对,我没有正确表达自己。我的意思是说,传递给rdd
(即,您希望在数据帧上执行的任何操作)的委托将在worker节点上运行。@lizarisk:您不能在转换或操作操作中使用任何未序列化的类,这些操作正在worker节点上运行。@Shankar我在这里没有看到任何在worker节点上运行的操作