SparkSQL自定义数据源读取数据库 类型转换问题

下面这一段是废话,时间紧的兄弟直接跳过:

前几天接触了 SparkSQL,通过自定义数据源可以完成各种数据库的读取和写入。我好像嗅到了数据中台的调调,封装一个扩展性强的小架架把 hbase,mysql,redis各种数据源都整合一下,再用并发多线程,对象池之类的优化一下性能,再招一个3000块的小表哥,多么优秀的开源节流,是不是又可以找老板涨工资了!考验架构能力的时候到了,不想当架构师的程序员不是一个好男人!好了,做梦时间结束,进入正题:

我们知道 hbase 最终是把数据转成了 HFile 文件,HFile 是 hadoop 的二进制格式文件,所以从 hbase 读出来的也是二进制字节流,那么要如何获取每个 Column 的数据类型呢?

其实这是一个伪命题,因为 SparkSQL 已经帮我们实现了!

在自定义数据源需要实现的第二层接口(DataSourceReader)需要实现这样一个方法:

override def readSchema(): StructType = {
    structType
  }

StructType 就是保存字段类型信息的,进入 DataSourceReader 接口看下方法调用:

private lazy val readerFactories: java.util.List[DataReaderFactory[UnsafeRow]] = reader match {
    case r: SupportsScanUnsafeRow => r.createUnsafeRowReaderFactories()
    case _ =>
      reader.createDataReaderFactories().asScala.map {
        new RowToUnsafeRowDataReaderFactory(
_, 
reader.readSchema() // here
): DataReaderFactory[UnsafeRow]
      }.asJava
  }

进入 RowToUnsafeRowDataReaderFactory 

class RowToUnsafeRowDataReaderFactory(rowReaderFactory: DataReaderFactory[Row], schema: StructType)
  extends DataReaderFactory[UnsafeRow] {

  override def preferredLocations: Array[String] = rowReaderFactory.preferredLocations

  override def createDataReader: DataReader[UnsafeRow] = {
    new RowToUnsafeDataReader(
      rowReaderFactory.createDataReader, // 这里的 Reader 就是我们自己实现的 DataReader
      RowEncoder.apply(schema).resolveAndBind() // 这是 column 的类型信息
    )
  }
}

继续进入 RowToUnsafeDataReader 

class RowToUnsafeDataReader(val rowReader: DataReader[Row], encoder: ExpressionEncoder[Row])
  extends DataReader[UnsafeRow] {

  override def next: Boolean = rowReader.next

  /**
    * rowReader 就是我们自己实现的 DataReader 对象
    * 这里他用了一个对象代理了我们自己实现的 DataReader 对象
    * get() 的时候 用具有 column 类型信息的 encoder 对我们返回的 Row 对象做了一次转换
    */
  override def get: UnsafeRow = encoder.toRow(rowReader.get).asInstanceOf[UnsafeRow]

  override def close(): Unit = rowReader.close()
}

而我们自定义数据源重写 DataReader 的时候,只需要获取一个数组对象array,然后直接调用 Row.fromSeq(array) 转成 Row 对象返回即可

override def get(): Row = {
    // 将查询的数据直接生成 Iterator 对象,get()直接iterator.next
    val result: Result = datas.next() 

    // 拆分 hbase 的列族和列名,获取结果,将结果封装成一个 String 数组
    val strings: Array[String] = cfcc.split(",").map(eachCfcc => {
      val strings: Array[String] = eachCfcc.trim.split(":")
      // 直接转 String
      Bytes.toString(result.getValue(strings(0).trim.getBytes(), strings(1).trim.getBytes()))
    })
    Row.fromSeq(strings)
  }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值