【spark原理系列】Spark Encoders原理示例源码分析

Spark Encoders原理示例源码分析

原理

Encoders对象中的方法使用了Spark的表达式编码器(ExpressionEncoder)来实现数据的编码和解码。这些编码器通过将数据转换为二进制格式,以便在Spark中进行高效的处理和优化。

编码器的工作原理如下:

  1. 基本类型编码器:对于基本类型(如整数、浮点数、布尔值等),编码器直接将其转换为对应的二进制表示。

  2. 字符串类型编码器:字符串类型的编码器将字符串转换为字节数组,并存储为二进制格式。

  3. 十进制类型编码器:十进制类型的编码器将十进制数转换为字节数组,并存储为二进制格式。

  4. 日期和时间类型编码器:日期和时间类型的编码器将日期和时间数据转换为对应的Java类型,并将其序列化为字节数组存储为二进制格式。

  5. 数组和集合类型编码器:数组和集合类型的编码器将数组或集合中的元素逐个进行编码,并将它们存储为二进制格式。

  6. Java Bean类型编码器:Java Bean类型的编码器通过反射获取Java Bean的属性值,并将其逐个编码为对应的二进制格式。

  7. 元组和样例类类型编码器:元组和样例类类型的编码器将其中的字段逐个进行编码,并将它们存储为二进制格式。

  8. Kryo和Java通用序列化编码器:Kryo和Java通用序列化编码器将对象使用对应的序列化方式进行序列化,并将其存储为字节数组的二进制格式。

在数据集创建时,我们可以指定相应的编码器来将数据转换为二进制格式。然后,这些二进制数据会被传递给Spark进行处理和优化。当需要访问和操作数据时,Spark会根据编码器的定义解码二进制数据,将其转换回原始数据类型。

通过使用合适的编码器,Spark可以在运行时高效地处理和优化数据,减少了数据的序列化和反序列化开销,并提高了计算性能和资源利用率。

方法总结

以下是Encoders对象中的方法总结:

  • BOOLEAN:返回一个可空布尔类型的编码器。
  • BYTE:返回一个可空字节类型的编码器。
  • SHORT:返回一个可空短整数类型的编码器。
  • INT:返回一个可空整数类型的编码器。
  • LONG:返回一个可空长整数类型的编码器。
  • FLOAT:返回一个可空浮点数类型的编码器。
  • DOUBLE:返回一个可空双精度浮点数类型的编码器。
  • STRING:返回一个可空字符串类型的编码器。
  • DECIMAL:返回一个可空十进制数类型的编码器。
  • DATE:返回一个可空日期类型的编码器。
  • TIMESTAMP:返回一个可空时间戳类型的编码器。
  • BINARY:返回一个字节数组类型的编码器。
  • bean:根据Java Bean类型创建一个编码器。
  • kryo:使用Kryo序列化方式创建一个编码器。
  • javaSerialization:使用Java通用序列化方式创建一个编码器。
  • tuple:用于不同元素个数的元组类型的编码器。
  • product:用于Scala的Product类型(元组、样例类等)的编码器。
  • scalaInt:返回一个用于Scala的基本整数类型(Int)的编码器。
  • scalaLong:返回一个用于Scala的基本长整数类型(Long)的编码器。
  • scalaDouble:返回一个用于Scala的基本双精度浮点数类型(Double)的编码器。
  • scalaFloat:返回一个用于Scala的基本浮点数类型(Float)的编码器。
  • scalaByte:返回一个用于Scala的基本字节类型(Byte)的编码器。
  • scalaShort:返回一个用于Scala的基本短整数类型(Short)的编码器。
  • scalaBoolean:返回一个用于Scala的基本布尔类型(Boolean)的编码器。

这些方法提供了一系列编码器,用于将不同类型的数据转换为二进制格式以便在Spark中进行处理和优化。使用这些编码器可以方便地将数据集转换为Dataset对象,并进行各种操作和转换。

示例

package org.example

import org.apache.spark.sql.{Encoder, Encoders, SparkSession}

object EncoderTest {
  val spark = SparkSession.builder.master("local[2]").appName("appName").config("", true).getOrCreate()
 
  case class Person(name: String, age: Int)
  def main(args: Array[String]): Unit = {
    import org.apache.spark.sql.{Encoder, Encoders}
    import java.math.BigDecimal
    import java.sql.{Date, Timestamp}

    val booleanEncoder: Encoder[java.lang.Boolean] = Encoders.BOOLEAN
    val booleanData: java.lang.Boolean = true
    val booleanDataset = spark.createDataset(Seq(booleanData))(booleanEncoder)
    booleanDataset.show()
    // 输出结果:
    // +-----+
    // |value|
    // +-----+
    // | true|
    // +-----+

    val byteEncoder: Encoder[java.lang.Byte] = Encoders.BYTE
    val byteData: java.lang.Byte = 10.toByte
    val byteDataset = spark.createDataset(Seq(byteData))(byteEncoder)
    byteDataset.show()
    // 输出结果:
    // +-----+
    // |value|
    // +-----+
    // |   10|
    // +-----+

    val shortEncoder: Encoder[java.lang.Short] = Encoders.SHORT
    val shortData: java.lang.Short = 100.toShort
    val shortDataset = spark.createDataset(Seq(shortData))(shortEncoder)
    shortDataset.show()
    // 输出结果:
    // +-----+
    // |value|
    // +-----+
    // |  100|
    // +-----+

    val intEncoder: Encoder[java.lang.Integer] = Encoders.INT
    val intData: java.lang.Integer = 1000
    val intDataset = spark.createDataset(Seq(intData))(intEncoder)
    intDataset.show()
    // 输出结果:
    // +-----+
    // |value|
    // +-----+
    // | 1000|
    // +-----+

    val longEncoder: Encoder[java.lang.Long] = Encoders.LONG
    val longData: java.lang.Long = 100000L
    val longDataset = spark.createDataset(Seq(longData))(longEncoder)
    longDataset.show()
    // 输出结果:
    // +------+
    // | value|
    // +------+
    // |100000|
    // +------+

    val floatEncoder: Encoder[java.lang.Float] = Encoders.FLOAT
    val floatData: java.lang.Float = 3.14f
    val floatDataset = spark.createDataset(Seq(floatData))(floatEncoder)
    floatDataset.show()
    // 输出结果:
    // +-----+
    // |value|
    // +-----+
    // | 3.14|
    // +-----+

    val doubleEncoder: Encoder[java.lang.Double] = Encoders.DOUBLE
    val doubleData: java.lang.Double = 3.14159
    val doubleDataset = spark.createDataset(Seq(doubleData))(doubleEncoder)
    doubleDataset.show()
    // 输出结果:
    // +-------+
    // |  value|
    // +-------+
    // |3.14159|
    // +-------+

    val stringEncoder: Encoder[java.lang.String] = Encoders.STRING
    val stringData: java.lang.String = "Hello, World!"
    val stringDataset = spark.createDataset(Seq(stringData))(stringEncoder)
    stringDataset.show()
    // 输出结果:
    // +-------------+
    // |        value|
    // +-------------+
    // |Hello, World!|
    // +-------------+

    val decimalEncoder: Encoder[BigDecimal] = Encoders.DECIMAL
    val decimalData: BigDecimal = new BigDecimal("1234.5678")
    val decimalDataset = spark.createDataset(Seq(decimalData))(decimalEncoder)
    decimalDataset.show()
    // 输出结果:
    // +---------+
    // |    value|
    // +---------+
    // |1234.5678|
    // +---------+

    val dateEncoder: Encoder[Date] = Encoders.DATE
    val dateData: Date = Date.valueOf("2022-01-01")
    val dateDataset = spark.createDataset(Seq(dateData))(dateEncoder)
    dateDataset.show()
    // 输出结果:
    // +----------+
    // |     value|
    // +----------+
    // |2022-01-01|
    // +----------+

    val timestampEncoder: Encoder[Timestamp] = Encoders.TIMESTAMP
    val timestampData: Timestamp = Timestamp.valueOf("2022-01-01 12:34:56")
    val timestampDataset = spark.createDataset(Seq(timestampData))(timestampEncoder)
    timestampDataset.show()
    // 输出结果:
    // +-------------------+
    // |              value|
    // +-------------------+
    // |2022-01-01 12:34:56|
    // +-------------------+

    val binaryEncoder: Encoder[Array[Byte]] = Encoders.BINARY
    val binaryData: Array[Byte] = Array(1, 2, 3, 4, 5)
    val binaryDataset = spark.createDataset(Seq(binaryData))(binaryEncoder)
    binaryDataset.show()
    // 输出结果:
    // +------------------+
    // |             value|
    // +------------------+
    // |[B@{memory address}|
    // +------------------+

    val personEncoder: Encoder[Person] = Encoders.product[Person]
    val personData: Seq[Person] = Seq(Person("Alice", 25), Person("Bob", 30))
    val personDataset = spark.createDataset(personData)(personEncoder)
    personDataset.show()
    // 输出结果:
    // +-----+---+
    // | name|age|
    // +-----+---+
    // |Alice| 25|
    // |  Bob| 30|
    // +-----+---+

    val tupleEncoder: Encoder[(Int, String)] = Encoders.tuple(Encoders.scalaInt, Encoders.STRING)
    val tupleData: Seq[(Int, String)] = Seq((1, "One"), (2, "Two"), (3, "Three"))
    val tupleDataset = spark.createDataset(tupleData)(tupleEncoder)
    tupleDataset.show()
    // 输出结果:
    // +---+-----+
    // | _1|   _2|
    // +---+-----+
    // |  1|  One|
    // |  2|  Two|
    // |  3|Three|
    // +---+-----+
  }
}

以上示例展示了使用Encoders对象中的方法创建不同类型数据的编码器,并将数据转换为Dataset后进行展示。输出结果显示了相应的数据集内容。注意,二进制类型(如字节数组)的输出结果显示了内存地址。

中文源码

object Encoders {

  /**
   * 用于可空的布尔类型的编码器。
   * Scala原始编码器可使用[[scalaBoolean]]。
   * @since 1.6.0
   */
  def BOOLEAN: Encoder[java.lang.Boolean] = ExpressionEncoder()

  /**
   * 用于可空的字节类型的编码器。
   * Scala原始编码器可使用[[scalaByte]]。
   * @since 1.6.0
   */
  def BYTE: Encoder[java.lang.Byte] = ExpressionEncoder()

  /**
   * 用于可空的短整型类型的编码器。
   * Scala原始编码器可使用[[scalaShort]]。
   * @since 1.6.0
   */
  def SHORT: Encoder[java.lang.Short] = ExpressionEncoder()

  /**
   * 用于可空的整型类型的编码器。
   * Scala原始编码器可使用[[scalaInt]]。
   * @since 1.6.0
   */
  def INT: Encoder[java.lang.Integer] = ExpressionEncoder()

  /**
   * 用于可空的长整型类型的编码器。
   * Scala原始编码器可使用[[scalaLong]]。
   * @since 1.6.0
   */
  def LONG: Encoder[java.lang.Long] = ExpressionEncoder()

  /**
   * 用于可空的浮点型类型的编码器。
   * Scala原始编码器可使用[[scalaFloat]]。
   * @since 1.6.0
   */
  def FLOAT: Encoder[java.lang.Float] = ExpressionEncoder()

  /**
   * 用于可空的双精度型类型的编码器。
   * Scala原始编码器可使用[[scalaDouble]]。
   * @since 1.6.0
   */
  def DOUBLE: Encoder[java.lang.Double] = ExpressionEncoder()

  /**
   * 用于可空的字符串类型的编码器。
   *
   * @since 1.6.0
   */
  def STRING: Encoder[java.lang.String] = ExpressionEncoder()

  /**
   * 用于可空的十进制数类型的编码器。
   *
   * @since 1.6.0
   */
  def DECIMAL: Encoder[java.math.BigDecimal] = ExpressionEncoder()

  /**
   * 用于可空的日期类型的编码器。
   *
   * @since 1.6.0
   */
  def DATE: Encoder[java.sql.Date] = ExpressionEncoder()

  /**
   * 用于可空的时间戳类型的编码器。
   *
   * @since 1.6.0
   */
  def TIMESTAMP: Encoder[java.sql.Timestamp] = ExpressionEncoder()

  /**
   * 用于字节数组的编码器。
   *
   * @since 1.6.1
   */
  def BINARY: Encoder[Array[Byte]] = ExpressionEncoder()

  /**
   * 创建一个Java Bean类型为T的编码器。
   *
   * T必须是公共可访问的。
   *
   * Java Bean字段支持的类型有:
   *  - 基本类型:boolean、int、double等。
   *  - 包装类型:Boolean、Integer、Double等。
   *  - 字符串类型:String。
   *  - java.math.BigDecimal、java.math.BigInteger。
   *  - 时间相关类型:java.sql.Date、java.sql.Timestamp。
   *  - 集合类型:目前只支持数组和java.util.List,对于map的支持正在进行中。
   *  - 嵌套的Java Bean。
   *
   * @since 1.6.0
   */
  def bean[T](beanClass: Class[T]): Encoder[T] = ExpressionEncoder.javaBean(beanClass)

  /**
   * (特定于Scala)创建一个使用Kryo序列化对象类型为T的编码器。
   * 此编码器将T映射到单个字节数组(二进制)字段。
   *
   * T必须是公共可访问的。
   *
   * @since 1.6.0
   */
  def kryo[T: ClassTag]: Encoder[T] = genericSerializer(useKryo = true)

  /**
   * 创建一个使用Kryo序列化对象类型为T的编码器。
   * 此编码器将T映射到单个字节数组(二进制)字段。
   *
   * T必须是公共可访问的。
   *
   * @since 1.6.0
   */
  def kryo[T](clazz: Class[T]): Encoder[T] = kryo(ClassTag[T](clazz))

  /**
   * (特定于Scala)创建一个使用通用Java序列化方法序列化对象类型为T的编码器。
   * 此编码器将T映射到单个字节数组(二进制)字段。
   *
   * T必须是公共可访问的。
   *
   * @note 这是极其低效的,只能作为最后的选择使用。
   *
   * @since 1.6.0
   */
  def javaSerialization[T: ClassTag]: Encoder[T] = genericSerializer(useKryo = false)

  /**
   * 创建一个使用通用Java序列化方法序列化对象类型为T的编码器。
   * 此编码器将T映射到单个字节数组(二进制)字段。
   *
   * T必须是公共可访问的。
   *
   * @note 这是极其低效的,只能作为最后的选择使用。
   *
   * @since 1.6.0
   */
  def javaSerialization[T](clazz: Class[T]): Encoder[T] = javaSerialization(ClassTag[T](clazz))

  /** 如果T不是公共类,则抛出异常。 */
  private def validatePublicClass[T: ClassTag](): Unit = {
    if (!Modifier.isPublic(classTag[T].runtimeClass.getModifiers)) {
      throw new UnsupportedOperationException(
        s"${classTag[T].runtimeClass.getName} 不是公共类。" +
          "只支持公共类。")
    }
  }

  /** 使用通用序列化器构建编码器的一种方法。 */
  private def genericSerializer[T: ClassTag](useKryo: Boolean): Encoder[T] = {
    if (classTag[T].runtimeClass.isPrimitive) {
      throw new UnsupportedOperationException("不支持基本类型。")
    }

    validatePublicClass[T]()

    ExpressionEncoder[T](
      schema = new StructType().add("value", BinaryType),
      flat = true,
      serializer = Seq(
        EncodeUsingSerializer(
          BoundReference(0, ObjectType(classOf[AnyRef]), nullable = true), kryo = useKryo)),
      deserializer =
        DecodeUsingSerializer[T](
          Cast(GetColumnByOrdinal(0, BinaryType), BinaryType),
          classTag[T],
          kryo = useKryo),
      clsTag = classTag[T]
    )
  }

  /**
   * 用于二元组的编码器。
   *
   * @since 1.6.0
   */
  def tuple[T1, T2](
    e1: Encoder[T1],
    e2: Encoder[T2]): Encoder[(T1, T2)] = {
    ExpressionEncoder.tuple(encoderFor(e1), encoderFor(e2))
  }

  /**
   * 用于三元组的编码器。
   *
   * @since 1.6.0
   */
  def tuple[T1, T2, T3](
    e1: Encoder[T1],
    e2: Encoder[T2],
    e3: Encoder[T3]): Encoder[(T1, T2, T3)] = {
    ExpressionEncoder.tuple(encoderFor(e1), encoderFor(e2), encoderFor(e3))
  }

  /**
   * 用于四元组的编码器。
   *
   * @since 1.6.0
   */
  def tuple[T1, T2, T3, T4](
    e1: Encoder[T1],
    e2: Encoder[T2],
    e3: Encoder[T3],
    e4: Encoder[T4]): Encoder[(T1, T2, T3, T4)] = {
    ExpressionEncoder.tuple(encoderFor(e1), encoderFor(e2), encoderFor(e3), encoderFor(e4))
  }

  /**
   * 用于五元组的编码器。
   *
   * @since 1.6.0
   */
  def tuple[T1, T2, T3, T4, T5](
    e1: Encoder[T1],
    e2: Encoder[T2],
    e3: Encoder[T3],
    e4: Encoder[T4],
    e5: Encoder[T5]): Encoder[(T1, T2, T3, T4, T5)] = {
    ExpressionEncoder.tuple(
      encoderFor(e1), encoderFor(e2), encoderFor(e3), encoderFor(e4), encoderFor(e5))
  }

  /**
   * 用于Scala的产品类型(元组、case类等)的编码器。
   * @since 2.0.0
   */
  def product[T <: Product : TypeTag]: Encoder[T] = ExpressionEncoder()

  /**
   * 用于Scala的原始整型类型的编码器。
   * @since 2.0.0
   */
  def scalaInt: Encoder[Int] = ExpressionEncoder()

  /**
   * 用于Scala的原始长整型类型的编码器。
   * @since 2.0.0
   */
  def scalaLong: Encoder[Long] = ExpressionEncoder()

  /**
   * 用于Scala的原始双精度型类型的编码器。
   * @since 2.0.0
   */
  def scalaDouble: Encoder[Double] = ExpressionEncoder()

  /**
   * 用于Scala的原始浮点型类型的编码器。
   * @since 2.0.0
   */
  def scalaFloat: Encoder[Float] = ExpressionEncoder()

  /**
   * 用于Scala的原始字节型类型的编码器。
   * @since 2.0.0
   */
  def scalaByte: Encoder[Byte] = ExpressionEncoder()

  /**
   * 用于Scala的原始短整型类型的编码器。
   * @since 2.0.0
   */
  def scalaShort: Encoder[Short] = ExpressionEncoder()

  /**
   * 用于Scala的原始布尔型类型的编码器。
   * @since 2.0.0
   */
  def scalaBoolean: Encoder[Boolean] = ExpressionEncoder()

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BigDataMLApplication

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值