SparkSQL基于DataSourceV2自定义数据源
版本说明:Spark 2.3
前言:之前在SparkSQL数据源操作文章中整理了一些SparkSQL内置数据源的使用,总的来说SparkSQL支持的数据源还是挺丰富的,但业务上可能不拘束于这几种数据源,比如将HBase作为SparkSQL的数据源,REST数据源等。这里主要讲一下在Spark2.3版本之后推出的DataSourceV2,基于DataSourceV2实现自定义数据源
1 DataSourceV1 VS DataSourceV2
自Spark1.3版本之后,引入了数据源API,我们可以实现自定义数据源。2.3版本之后又引入的新版API,关于V1与V2的区别以及使用可以参考https://blog.csdn.net/zjerryj/article/details/84922369与https://developer.ibm.com/code/2018/04/16/introducing-apache-spark-data-sources-api-v2/这两篇文章。这里简单的总结一下V1的缺点,以及V2的新特性。
1.1 DataSourceV1缺点
依赖上层API
难以添加新的优化算子
难以传递分区信息
缺少事务性的写操作
缺少列存储和流式计算支持
1.2 DataSourceV2优点
DataSourceV2 API使用Java编写
不依赖于上层API(DataFrame/RDD)
易于扩展,可以添加新的优化,同时保持向后兼容
提供物理信息,如大小、分区等
支持Streamin Source/Sink
灵活、强大和事务性的写入API
1.3 Spark2.3中V2的功能
支持列扫描和行扫描
列裁剪和过滤条件下推
可以提供基本统计和数据分区
事务写入API
支持微批和连续的Streaming Source/Sink
2 基于DataSourceV2实现输入源
SparkSQL的DataSourceV2的实现与StructuredStreaming自定义数据源如出一辙,思想是一样的,但是具体实现有所不同,主要步骤如下:
第一步:继承DataSourceV2和ReadSupport创建XXXDataSource类,重写ReadSupport的creatReader方法,用来返回自定义的DataSourceReader类,如返回自定义XXXDataSourceReader实例
第二步:继承DataSourceReader创建XXXDataSourceReader类,重写DataSourceReader的readSchema方法用来返回数据源的schema,重写DataSourceReader的createDataReaderFactories用来返回多个自定义DataReaderFactory实例
第三步:继承DataReaderFactory创建DataReader工厂类,如XXXDataReaderFactory,重写DataReaderFactory的createDataReader方法,返回自定义DataRader实例
第四步:继承DataReader类创建自定义的DataReader,如XXXDataReader,重写DataReader的next()方法,用来告诉Spark是否有下条数据,用来触发get()方法,重写DataReader的get()方法获取数据,重写DataReader的close()方法用来关闭资源
2.1 继承DataSourceV2和ReadSupport创建XXXDataSource类
这里以创建CustomDataSourceV2类为例
2.1.1 创建CustomDataSourceV2类
/**
* 创建DataSource提供类
* 1.继承DataSourceV2向Spark注册数据源
* 2.继承ReadSupport支持读数据
*/
class CustomDataSourceV2 extends DataSourceV2
with ReadSupport {
// todo
}
1
2
3
4
5
6
7
8
9
2.1.2 重写ReadSupport的createReader方法
该方法用来返回一个用户自定义的DataSourceReader实例
/**
* 创建Reader
*
* @param options 用户定义的options
* @return 自定义的DataSourceReader
*/
override def createReader(options: DataSourceOptions): DataSourceReader = new CustomDataSourceV2Reader(options)
1
2
3
4
5
6
7
2.2 继承DataSourceReader创建XXXDataSourceReader类
该类用来自定义DataSourceReader,需要继承DataSourceReader,并重写readSchema和createDataReaderFactories方法。
2.2.1 创建CustomDataSourceV2Reader类
/**
* 自定义的DataSourceReader
* 继承DataSourceReader
* 重写readSchema方法用来生成schema
* 重写createDataReaderFactories,用来根据条件,创建多个工厂实例
*
* @param options options
*/
class CustomDataSourceV2Reader(options: DataSourceOptions) extends DataSourceReader {
// Override some functions
}
1
2
3
4
5
6
7
8
9
10
11
2.2.2 重写DataSourceReader的readSchema方法
该方法用来返回数据源的schema
/**
* 生成schema
*
* @return schema
*/
override def readSchema(): StructType = ???
1
2
3
4
5
6
2.2.3 重写DataSourceReader的createDataReaderFactories方法
实现该方法,可以根据不同的条件,创建多个createDataReader工厂实例,用来并发获取数据?(暂且这么理解的,或者是按照分区获取数据?)
/**
* 创建DataReader工厂实例
*
* @return 多个工厂类实例
*/
override def createDataReaderFactories(): util.List[DataReaderFactory[Row]] = {
import collection.JavaConverters._
Seq(
new CustomDataSourceV2ReaderFactory().asInstanceOf[DataReaderFactory[Row]]
).asJava
}
1
2
3
4
5
6
7
8
9
10
11
2.3 继承DataReaderFactory创建DataReader工厂类
该类是DataReader的工厂来,用来返回DataReader实例
2.3.1 创建CustomDataSourceV2Factory类
/**
* 自定义DataReaderFactory类
*/
class CustomDataSourceV2ReaderFactory extends DataReaderFactory[Row] {
// Override some functions
}
1
2
3
4
5
6
7
2.3.2 重写DataReaderFactory的createDataReader方法
该方法用来实例化自定义的DataReader
/**
* 重写createDataReader方法,用来实例化自定义的DataReader
*
* @return 自定义的DataReader
*/
override def createDataReader(): DataReader[Row] = new CustomDataReader
1
2
3
4
5
6
2.4 继承DataReader类创建自定义的DataReader
该类为重点实现部分,用来自定义获取数据的方式
2.4.1 创建CustomDataReader类
/**
* 自定义DataReader类
*/
class CustomDataReader extends DataReader[Row] {
// Override some functions
}
1
2
3
4
5
6
2.4.2 重写CustomDataReader的next()方法
该方法返回一个布尔值,来告诉Spark是否含有下条数据,以便触发get()方法获取数据
/**
* 是否有下一条数据
*
* @return boolean
*/
override def next(): Boolean = ???
1
2
3
4
5
6
2.4.3 重写CustomDataReader的get()方法
该方法用来获取数据,返回类型是在继承DataReader时指定的泛型
/**
* 获取数据
* 当next为true时会调用get方法获取数据
*
* @return Row
*/
override def get(): Row = ???
1
2
3
4
5
6
7
2.4.4 重写CustomDataReader的close()方法
该方法用来关闭相应的资源
/**
* 关闭资源
*/
override def close(): Unit = ???
1
2
3
4
2.5 以REST为例,实现自定义的数据源
这里主要是从REST接口里获取JSON格式的数据,然后生成DataFrame数据源
2.5.1 创建RestDataSource类