为什么将Spark DataFrame转换为RDD需要完全重新映射?

时间:2019-01-19 17:13:39

标签: scala apache-spark

从Spark源代码:

/**
   * Represents the content of the Dataset as an `RDD` of `T`.
   *
   * @group basic
   * @since 1.6.0
   */
  lazy val rdd: RDD[T] = {
    val objectType = exprEnc.deserializer.dataType
    rddQueryExecution.toRdd.mapPartitions { rows =>
      rows.map(_.get(0, objectType).asInstanceOf[T])
    }
  }

https://github.com/apache/spark/blob/master/sql/core/src/main/scala/org/apache/spark/sql/Dataset.scala#L2972

mapPartitions可能需要很长的时间才能计算出RDD。因此,这使得诸如

这样的操作
df.rdd.getNumPartitions

非常昂贵。假设DataFrameDataSet[Row]并且DataSetRDD组成,为什么需要重新映射?任何见解都很感激。

1 个答案:

答案 0 :(得分:6)

TL; DR 这是因为内部RDD不是RDD[Row]

  

鉴于DataFrame为DataSet[Row],而DataSet由RDD组成

这太过简单了。首先DataSet[T]并不意味着您与T的容器进行交互。这意味着,如果您使用类似集合的API(通常称为强类型),则内部表示形式将被解码T中。

内部表示形式是Tungsten内部使用的一种二进制格式。这种表示形式是内部的,并且有变化的主题,并且太低了,无法在实践中使用。

公开此数据的中间表示形式是InternalRow-rddQueryExecution.toRDD实际上是RDD[InternalRow]。此表示形式(存在不同的实现)仍然公开内部类型,被视为“弱”私有的,因为o.a.s.sql.catalyst中的所有对象(访问没有明确限制,但未记录API),并且很难交互

在解码中发挥作用的原因以及为什么需要完整的“重新映射”-将内部(通常是不安全的)对象转换为供公共使用的外部类型。

最后,to reiterate是我的上一个语句-调用getNumPartitions时将不会执行有问题的代码。