Scala 为什么在RDD中,map提供NotSerializableException,而foreach不提供';T
我理解Scala 为什么在RDD中,map提供NotSerializableException,而foreach不提供';T,scala,serialization,apache-spark,rdd,Scala,Serialization,Apache Spark,Rdd,我理解map和foreach(懒惰和渴望)之间的基本区别,我也理解为什么会出现这个代码片段 sc.makeRDD(Seq("a", "b")).map(s => new java.io.ByteArrayInputStream(s.getBytes)).collect sc.makeRDD(Seq("a", "b")).foreach(s => { val is = new java.io.ByteArrayInputStream(s.getBytes) println("
map
和foreach
(懒惰和渴望)之间的基本区别,我也理解为什么会出现这个代码片段
sc.makeRDD(Seq("a", "b")).map(s => new java.io.ByteArrayInputStream(s.getBytes)).collect
sc.makeRDD(Seq("a", "b")).foreach(s => {
val is = new java.io.ByteArrayInputStream(s.getBytes)
println("is = " + is)
})
应该给
java.io.NotSerializableException:java.io.ByteArrayInputStream
然后我认为下面的代码片段也应该如此
sc.makeRDD(Seq("a", "b")).map(s => new java.io.ByteArrayInputStream(s.getBytes)).collect
sc.makeRDD(Seq("a", "b")).foreach(s => {
val is = new java.io.ByteArrayInputStream(s.getBytes)
println("is = " + is)
})
但这段代码运行良好。为什么会这样?实际上
map
和foreach
之间的根本区别不是评估策略。让我们看看签名(为了简洁起见,我省略了map
的隐式部分):
map
获取从T
到U
的函数,将其应用于现有RDD[T]
的每个元素,并返回RDD[U]
。要允许像shufflingU
这样的操作,必须是可序列化的
foreach
接受一个从T
到Unit
的函数(类似于Javavoid
),其本身不返回任何内容。一切都发生在本地,不涉及网络流量,因此不需要序列化。与map
不同,foreach
应该在想要获得某种副作用时使用,比如在
另一方面,这两者实际上是不同的。您在map
中使用的匿名函数是一个函数:
(s: String) => java.io.ByteArrayInputStream
您在foreach
中使用的
(s: String) => Unit
如果将第二个函数与
map
一起使用,您的代码将被编译,尽管结果与您想要的相差甚远(RDD[Unit]
)。collect
在map导致问题后调用。
下面是我在spark shell中的测试结果
由于无需将数据发送到其他节点,下面的过程将通过
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).count
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).first
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).collect
以下调用失败,因为映射输出可以发送到其他节点
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).count
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).first
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).collect
重新分区强制将数据分发到节点,但失败
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).repartition(2).saveAsTextFile("/tmp/NWRepart")
无需在下面重新分区调用通行证
sc.makeRDD(1 to 1000, 1).map(_ => {NullWritable.get}).saveAsTextFile("/tmp/NW")
很好的解释!我知道需要对
foreach
进行洗牌,但请您详细说明为什么在map
的情况下会涉及洗牌?这是因为生成的分区(在map之后)的大小可能比该机器中可用的磁盘大小大吗?老实说,我不确定是否仅map
就可以触发洗牌,但重要的是下游操作可能:rdd.map(f).重新分区(1)
。如果返回类型为f
则所有操作都失败。请澄清。可以在映射函数中使用不可序列化的类型。它不能是返回类型。