知乎排版不好,也可以去简书看:Spark load() 源码解析
问题描述
在使用spark读取HDFS上的数据时,经常使用load的方式(没有hive的情况下)
spark.read.schema(schema).load(data_path)
以前比较常见的是textFile读HDFS的方式,不同于此,load的方式可以直接形成DataFrame,使用上更方便一些。遇到的一个问题是在读取的目录下非常多的碎片文件时,1.load地方为什么单独形成了一个job?2.文件过多时直接OOM程序停止。
在网上能搜到的比较类似的问题:
Spark read.parquet takes too much time
出于好奇,让我们看看load的源码 (2.3.1)
源码追踪
load方法
首先点进load方法,发现是spark sql的包(org.apache.spark.sql)
/**
* Loads input in as a `DataFrame`, for data sources that support multiple paths.
* Only works if the source is a HadoopFsRelationProvider.
*
* @since 1.6.0
*/
@scala.annotation.varargs
def load(paths: String*): DataFrame = {
sparkSession.baseRelationToDataFrame(
DataSource.apply(
sparkSession,
paths = paths,
userSpecifiedSchema = userSpecifiedSchema,
className = source,
options = extraOptions.toMap).resolveRelation())
}
```
入参是目录,可以是多个,返回值是DataFrame。主要是调用了sparkSession.baseRelationToDataFrame
我们看一下这个baseRelationToDataFrame
```scala
/**
* Convert a `BaseRelation` created for external data sources into a `DataFrame`.
*
* @since 2.0.0
*/
def baseRelationToDataFrame(baseRelation: BaseRelation): DataFrame = {
Dataset.ofRows(self, LogicalRelation(baseRelation))
}
这个方法是使用一个BaseRelation来得到DataFrame。在load方法里,我们传入的就是一个BaseRelation的子类。那我们看看究竟传入了什么样的BaseRelation。
DataSource类
在load函数里,通过DataSource伴生类调用apply方法获取一个DataSource实例,然后使用resolveRelation()方法返回的BaseRelation,看来关键在这个DataSource类
对于这个类,网上也有一些他人的分享:
利用 Spark DataSource API 实现Rest数据源
Spark Data Source API: Extending Our Spark SQL Query Engine
这个类在spark读取数据部分十分重要,有接近一千行代码。
我们这里只看其中一小部分。
首先看一下传入伴生类的参数:
//sparkSes