Spark广播自定义BiMap类的序列化

时间:2015-05-05 16:59:55

标签: scala serialization apache-spark kryo

尝试制作由两个地图组成的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计算逆值) )。最后一点是确保反向是共享的,而不是由每个任务重新创建。

虽然这似乎有点工作但它有点难看,我仍然不确定一些事情:

  1. 是广播变量中分配的实例的所有存储,而不是任务堆空间?
  2. 为什么i必须是var并且在它永远不应为null时检查为null?
  3. 最重要的是,这是否导致在广播时间丢弃逆并在反序列化中重新创建?
  4. 我知道我需要在Kryo注册这个并最终实现KryoSerializable以精细控制序列化。

1 个答案:

答案 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

如果您想检查通过网络发送的内容,可以将序列化结果写入文件...