尝试制作由两个地图组成的Spark广播BiMap。由于映射在任一方向上都是唯一的,因此应该序列化的是单个Map,实际上只需要序列化Seq [(K,V)]。所以只是潜在的前向Map元素。在反序列化中,我们可以重新创建逆映射和索引。
这是拟议的设计:
class BiMap[K, V] (
private val m: Map[K, V],
// if this is serialized we allow i to be discarded and recalculated when deserialized
@transient private var i: Option[BiMap[V, K]] = None
) extends Serializable {
// NOTE: make inverse's inverse point back to current BiMap
// if this is serialized we allow inverse to be discarded and recalculated
// when first invoked from "val size_" in the constructor
@transient lazy val inverse: BiMap[V, K] = {
if( i == null.asInstanceOf[Option[BiMap[V, K]]] )
i = None
i.getOrElse {
val rev = m.map(_.swap)
require((rev.size == m.size), "Failed to create reversed map. Cannot have duplicated values.")
new BiMap(rev, Some(this))
}
}
// forces inverse to be calculated in the constructor when deserialized
// not when first used
@transient val size_ = inverse.size
...
}
虽然这似乎有效,但我无法确定为什么我必须检查i
是否为null,但在反序列化后它为空。最初它是一个val,默认初始化=无。
只有m应序列化,因此反函数为@transient lazy
,并且还有另一个@transient val size_ = inverse.size
,用于在反序列化时(而不是在任务调用时inverse
计算逆值) )。最后一点是确保反向是共享的,而不是由每个任务重新创建。
虽然这似乎有点工作但它有点难看,我仍然不确定一些事情:
i
必须是var并且在它永远不应为null时检查为null?我知道我需要在Kryo注册这个并最终实现KryoSerializable以精细控制序列化。
答案 0 :(得分:0)
我只能回答你的一些问题。
首先,将Kryo设置为默认序列化程序是不够的。您的数据是根据Java序列化框架进行序列化的,而Java序列化框架的效率远远低于Kryo。如果要使用Kryo,则还需要将"spark.kryo.registrator"
属性设置为registrator类的完整类名,该类必须实现KryoRegistrator
。这意味着有一个你可以轻松实现的方法(这是Java代码,但我相信你把它移植到Scala没有问题):
public void registerClasses(Kryo k) {
k.register(BiMap.class);
}
如果您愿意,可以通过Kryo使您的班级实现KryoSerializable
来管理序列化。有关详细信息,请阅读documentation。
如果您想检查通过网络发送的内容,可以将序列化结果写入文件...