1002数据源 DataSource

从 Spark 1.3.0 开始Spark 推出了 DataFrame 的 API与此同时 DataSource 也被引入到 Spark 中Spark 将文本文件CSV 文件JSON 文件等一系列格式的输入数据都作为数据源特质 DataSourceRegister 是对所有数据源的抽象DataSourceRegister 的所有具体实现都被注册到了 DataSource DataSource将负责对不同类型数据源的查找创建不同类型数据源的信息解析不同数据源的关系等

DataSourceRegister 详解

DataSourceRegister 是数据源的抽象,所有数据源都应该实现它。DataSourceRegister 的定义非常简单,代码如下。

trait DataSourceRegister {
  def shortName(): String
}

shortName 方法意在获取数据源提供者使用的格式或格式的别名。

 

override def shortName():String="text"

可以看到 TextFileFormat 的别名为 text此外其他 DataSourceRegister 的实现类也都有属于自己的别名

 

DataSource 详解

 

DataSource 用于表示在 Spark SQL 中可插拔的数据源。DataSource 中包括以下属性。

  • sparkSession:即 SparkSession。
  • className:此名称用于决定要使用的类。
  • paths:数据源的多个路径。
  • userSpecifiedSchema:用户指定的 StructType。
  • partitionColumns:分区字段的序列。
  • bucketSpec:类型为 BucketSpec。BucketSpec 是一种用于将数据集分解为更多可管理部分的技术。由于桶的数量是固定的,因此数据不会随数据而波动。
  • options:类型为 Map[String,String],用于保存选项。
  • catalogTable:类型为 CatalogTable。CatalogTable 是用于定义表的字典。
  • providingClass:数据源的类。providingClass 实际是以 className 为参数,调用 Data-Source 伴生对象的 lookupDataSource 方法(见代码清单 10-1)获得的。
  • sourceInfo:类型为 SourceInfo。样例类 SourceInfo 用于表示数据源的名称和元数据信息,其定义如下。

 

case class SourceInfo(name: String, schema: StructType, partitionColumns: Seq[String])

 

DataSource 伴生对象的 backwardCompatibilityMap 属性缓存了类名与对应的DataSource-Register 实现类之间的映射关系代码如下

 

private val backwardCompatibilityMap: Map[String, String] = {
  val jdbc = classOf[JdbcRelationProvider].getCanonicalName
  val json = classOf[JsonFileFormat].getCanonicalName
  val parquet = classOf[ParquetFileFormat].getCanonicalName
  val csv = classOf[CSVFileFormat].getCanonicalName
  val libsvm = "org.apache.spark.ml.source.libsvm.LibSVMFileFormat"
  val orc = "org.apache.spark.sql.hive.orc.OrcFileFormat"

  Map(
    "org.apache.spark.sql.jdbc" -> jdbc,
    "org.apache.spark.sql.jdbc.DefaultSource" -> jdbc,
    "org.apache.spark.sql.execution.datasources.jdbc.DefaultSource" -> jdbc,
    "org.apache.spark.sql.execution.datasources.jdbc" -> jdbc,
    "org.apache.spark.sql.json" -> json,
    "org.apache.spark.sql.json.DefaultSource" -> json,
    "org.apache.spark.sql.execution.datasources.json" -> json,
    "org.apache.spark.sql.execution.datasources.json.DefaultSource" -> json,
    "org.apache.spark.sql.parquet" -> parquet,
    "org.apache.spark.sql.parquet.DefaultSource" -> parquet,
    "org.apache.spark.sql.execution.datasources.parquet" -> parquet,
    "org.apache.spark.sql.execution.datasources.parquet.DefaultSource" -> parquet,
    "org.apache.spark.sql.hive.orc.DefaultSource" -> orc,
    "org.apache.spark.sql.hive.orc" -> orc,
    "org.apache.spark.ml.source.libsvm.DefaultSource" -> libsvm,
    "org.apache.spark.ml.source.libsvm" -> libsvm,
    "com.databricks.spark.csv" -> csv
  )
}

lookupDataSource方法

DataSource 的伴生对象中提供了根据指定的名字找到对应的数据源类的方法lookup-DataSource

def lookupDataSource(provider: String): Class[_] = {
  val provider1 = backwardCompatibilityMap.getOrElse(provider, provider)
  val provider2 = s"$provider1.DefaultSource"
  val loader = Utils.getContextOrSparkClassLoader
  val serviceLoader = ServiceLoader.load(classOf[DataSourceRegister], loader)

  try {
    serviceLoader.asScala.filter(_.shortName().equalsIgnoreCase(provider1)).toList match {
      case Nil =>
        try {
          Try(loader.loadClass(provider1)).orElse(Try(loader.loadClass(provider2))) match {
            case Success(dataSource) =>
              dataSource
            case Failure(error) =>
              // 忽略抛出异常的代码
          }
        } catch {
          // 忽略捕获异常的处理代码
        }
      case head :: Nil =>
        head.getClass
      case sources =>
        sys.error(s"Multiple sources found for $provider1 " +
          s"(${sources.map(_.getClass.getName).mkString(", ")}), " +
          "please specify the fully qualified class name.")
    }
  } catch {
      // 忽略捕获异常的处理代码
  }
}

lookupDataSource 方法的执行步骤如下。

  1. 从 backwardCompatibilityMap 中查找指定名称对应的数据源类 provider1,还在 provider1 后拼接 DefaultSource 构成 provider2。
  2. 由于所有的数据源类都必须实现 DataSourceRegister,且 DataSourceRegister的 shortName 方法定义了获取数据源类的别名的规范,所以这里使用类加载器找到与 provider1 匹配的数据源类,如果未能匹配,则尝试加载由 provider1 或 provider2 指定的类。

sourceSchema

sourceSchema 方法用于根据 providingClass 得到对应的 SourceInfo,sourceSchema 方法通过反射得到 providingClass 对应的实例然后根据实例的不同类型构造 SourceInfo

resolveRelation

resolveRelation 方法用于根据数据源的提供类providingClassuserSpecifiedSchema 创建读写数据源所需的 BaseRelation由于resolveRelation 方法的内容很多这里只展示 resolveRelation 方法的一部分内容

def resolveRelation(checkFilesExist: Boolean = true): BaseRelation = {
  val relation = (providingClass.newInstance(), userSpecifiedSchema) match {
    case (dataSource: SchemaRelationProvider, Some(schema)) =>
      dataSource.createRelation(sparkSession.sqlContext, caseInsensitiveOptions, schema)
    case (dataSource: RelationProvider, None) =>
      dataSource.createRelation(sparkSession.sqlContext, caseInsensitiveOptions)
    case (_: SchemaRelationProvider, None) =>
      // 忽略创建BaseRelation的代码
    case (format: FileFormat, _) =>
      // 忽略创建BaseRelation的代码
    case _ =>
      throw new AnalysisException(
        s"$className is not a valid Spark SQL Data Source.")
  }
  relation
}

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值