http://blog.csdn.net/oopsoom/article/details/42061077

一、Spark SQL External DataSource简介

随着Spark1.2的发布,Spark SQL开始正式支持外部数据源。Spark SQL开放了一系列接入外部数据源的接口,来让开发者可以实现。

这使得Spark SQL支持了更多的类型数据源,如json, parquet, avro, csv格式。只要我们愿意,我们可以开发出任意的外部数据源来连接到Spark SQL。之前大家说的支持HBASE,Cassandra都可以用外部数据源的方式来实现无缝集成。

二、External DataSource

拿Spark1.2的json为例,它支持已经改为了实现了外部数据源的接口方式。所以除了先前我们操作json的API,又多了一种DDL创建外部数据源的方式。

parquetFile的操作方式也如下类似,就不一一列举了。

2.1 SQL方式 CREATE TEMPORARY TABLE USING OPTIONS

在Spark1.2之后,支持了一种CREATE TEMPORARY TABLE USING OPTIONS的DDL语法来创建外部数据源的表。

  1. CREATE TEMPORARY TABLE jsonTable
  2. USING org.apache.spark.sql.json
  3. OPTIONS (
  4. path '/path/to/data.json'
  5. )

1、操作示例:

我们拿example下people.json文件来做示例。

  1. shengli-mac$ cat /Users/shengli/git_repos/spark/examples/src/main/resources/people.json
  2. {"name":"Michael"}
  3. {"name":"Andy", "age":30}
  4. {"name":"Justin", "age":19}

2、DDL创建外部数据源表jsonTable:

  1. 14/12/21 16:32:14 INFO repl.SparkILoop: Created spark context..
  2. Spark context available as sc.
  3.  
  4. scala> import org.apache.spark.sql.SQLContext
  5. import org.apache.spark.sql.SQLContext
  6.  
  7. scala> val sqlContext = new SQLContext(sc)
  8. sqlContext: org.apache.spark.sql.SQLContext = org.apache.spark.sql.SQLContext@7be62956
  9.  
  10. scala> import sqlContext._
  11. import sqlContext._
  12. //创建jsonTable外部数据源表,并且指定其数数据源文件是people.json这个json文件,同时指定使用org.apache.spark.sql.json该类型的隐式转化类(这个后续文章会介绍)
  13. scala> val jsonDDL = s"""
  14. | |CREATE TEMPORARY TABLE jsonTable
  15. | |USING org.apache.spark.sql.json
  16. | |OPTIONS (
  17. | | path 'file:///Users/shengli/git_repos/spark/examples/src/main/resources/people.json'
  18. | |)""".stripMargin
  19. jsonDDL: String =
  20. "
  21. CREATE TEMPORARY TABLE jsonTable
  22. USING org.apache.spark.sql.json
  23. OPTIONS (
  24. path 'file:///Users/shengli/git_repos/spark/examples/src/main/resources/people.json'
  25. )"
  26.  
  27. scala> sqlContext.sql(jsonDDL).collect() //创建该外部数据源表jsonTable
  28. 14/12/21 16:44:27 INFO scheduler.DAGScheduler: Job 0 finished: reduce at JsonRDD.scala:57, took 0.204461 s
  29. res0: Array[org.apache.spark.sql.Row] = Array()

我们来看下该schemaRDD:

  1. scala> val jsonSchema = sqlContext.sql(jsonDDL)
  2. jsonSchema: org.apache.spark.sql.SchemaRDD =
  3. SchemaRDD[7] at RDD at SchemaRDD.scala:108
  4. == Query Plan ==
  5. == Physical Plan ==
  6. ExecutedCommand (CreateTableUsing jsonTable, org.apache.spark.sql.json, Map(path -> file:///Users/shengli/git_repos/spark/examples/src/main/resources/people.json))

ExecutedCommand来取把数据用spark.sql.json的方式从path加载到jsonTable中。涉及到得类是CreateTableUsing,后续源码分析会讲到。

各阶段执行计划情况:

  1. scala> sqlContext.sql("select * from jsonTable").queryExecution
  2. res6: org.apache.spark.sql.SQLContext#QueryExecution =
  3. == Parsed Logical Plan ==
  4. 'Project [*]
  5. 'UnresolvedRelation None, jsonTable, None
  6.  
  7. == Analyzed Logical Plan ==
  8. Project [age#0,name#1]
  9. Relation[age#0,name#1] JSONRelation(file:///Users/shengli/git_repos/spark/examples/src/main/resources/people.json,1.0)
  10.  
  11. == Optimized Logical Plan ==
  12. Relation[age#0,name#1] JSONRelation(file:///Users/shengli/git_repos/spark/examples/src/main/resources/people.json,1.0)
  13.  
  14. == Physical Plan ==
  15. PhysicalRDD [age#0,name#1], MapPartitionsRDD[27] at map at JsonRDD.scala:47
  16.  
  17. Code Generation: false
  18. == RDD ==

至此,创建加载外部数据源到Spark SQL已经完成。

我们可以使用任何我们希望的方式来查询:

3、SQL查询方式:

  1. scala> sqlContext.sql("select * from jsonTable")
  2. 21 16:52:13 INFO spark.SparkContext: Created broadcast 6 from textFile at JSONRelation.scala:39
  3. res2: org.apache.spark.sql.SchemaRDD =
  4. SchemaRDD[20] at RDD at SchemaRDD.scala:108
  5. == Query Plan ==
  6. == Physical Plan ==
  7. PhysicalRDD [age#2,name#3], MapPartitionsRDD[24] at map at JsonRDD.scala:47

执行查询:

  1. scala> sqlContext.sql("select * from jsonTable").collect()
  2. res1: Array[org.apache.spark.sql.Row] = Array([null,Michael], [30,Andy], [19,Justin])

2.2 API方式

sqlContext.jsonFile

  1. scala> val json = sqlContext.jsonFile("file:///Users/shengli/git_repos/spark/examples/src/main/resources/people.json")
  2. scala> json.registerTempTable("jsonFile")
  3.  
  4. scala> sql("select * from jsonFile").collect()
  5. res2: Array[org.apache.spark.sql.Row] = Array([null,Michael], [30,Andy], [19,Justin])

总的来说,Spark SQL 在努力的向各种数据源靠拢,希望让Spark SQL能和其它许多类型的数据源的集成。

Spark SQL提供的了一种创建加载外部数据源表的DDL语法:CREATE TEMPORARY TABLE USING OPTIONS

Spark SQL对外开放了一系列的扩展接口,能够通过实现这些接口,来实现对不同的数据源接入,如avro, csv, parquet,json, etc

三、Sources包核心

Spark SQL在Spark1.2中提供了External DataSource API,开发者可以根据接口来实现自己的外部数据源,如avro, csv, json, parquet等等。

在Spark SQL源代码的org/spark/sql/sources目录下,我们会看到关于External DataSource的相关代码。这里特别介绍几个:

1、DDLParser 

专门负责解析外部数据源SQL的SqlParser,解析create temporary table xxx using options (key 'value', key 'value') 创建加载外部数据源表的语句。

  1. protected lazy val createTable: Parser[LogicalPlan] =
  2. CREATE ~ TEMPORARY ~ TABLE ~> ident ~ (USING ~> className) ~ (OPTIONS ~> options) ^^ {
  3. case tableName ~ provider ~ opts =>
  4. CreateTableUsing(tableName, provider, opts)
  5. }

2、CreateTableUsing

一个RunnableCommand,通过反射从外部数据源lib中实例化Relation,然后注册到为temp table。

  1. private[sql] case class CreateTableUsing(
  2. tableName: String,
  3. provider: String, // org.apache.spark.sql.json
  4. options: Map[String, String]) extends RunnableCommand {
  5.  
  6. def run(sqlContext: SQLContext) = {
  7. val loader = Utils.getContextOrSparkClassLoader
  8. val clazz: Class[_] = try loader.loadClass(provider) catch { //do reflection
  9. case cnf: java.lang.ClassNotFoundException =>
  10. try loader.loadClass(provider + ".DefaultSource") catch {
  11. case cnf: java.lang.ClassNotFoundException =>
  12. sys.error(s"Failed to load class for data source: $provider")
  13. }
  14. }
  15. val dataSource = clazz.newInstance().asInstanceOf[org.apache.spark.sql.sources.RelationProvider] //json包DefaultDataSource
  16. val relation = dataSource.createRelation(sqlContext, new CaseInsensitiveMap(options))//创建JsonRelation
  17.  
  18. sqlContext.baseRelationToSchemaRDD(relation).registerTempTable(tableName)//注册
  19. Seq.empty
  20. }
  21. }  

    2、DataSourcesStrategy

在 Strategy 一文中,我已讲过Streategy的作用,用来Plan生成物理计划的。这里提供了一种专门为了解析外部数据源的策略。

最后会根据不同的BaseRelation生产不同的PhysicalRDD。不同的BaseRelation的scan策略下文会介绍。

  1. private[sql] object DataSourceStrategy extends Strategy {
  2. def apply(plan: LogicalPlan): Seq[SparkPlan] = plan match {
  3. case PhysicalOperation(projectList, filters, l @ LogicalRelation(t: CatalystScan)) =>
  4. pruneFilterProjectRaw(
  5. l,
  6. projectList,
  7. filters,
  8. (a, f) => t.buildScan(a, f)) :: Nil
  9. ......
  10. case l @ LogicalRelation(t: TableScan) =>
  11. execution.PhysicalRDD(l.output, t.buildScan()) :: Nil
  12.  
  13. case _ => Nil
  14. }

3、interfaces.scala

该文件定义了一系列可扩展的外部数据源接口,对于想要接入的外部数据源,我们只需实现该接口即可。里面比较重要的trait RelationProvider 和 BaseRelation,下文会详细介绍。

4、filters.scala

该Filter定义了如何在加载外部数据源的时候,就进行过滤。注意哦,是加载外部数据源到Table里的时候,而不是Spark里进行filter。这个有点像hbase的coprocessor,查询过滤在Server上就做了,不在Client端做过滤。

5、LogicalRelation

封装了baseRelation,继承了catalyst的LeafNode,实现MultiInstanceRelation。

四、External DataSource注册流程

用spark sql下sql/json来做示例, 画了一张流程图,如下:
 
 
注册外部数据源的表的流程:
1、提供一个外部数据源文件,比如json文件。
2、提供一个实现了外部数据源所需要的interfaces的类库,比如sql下得json包,在1.2版本后改为了External Datasource实现。
3、引入SQLContext,使用DDL创建表,如create temporary table xxx using options (key 'value', key 'value') 
4、External Datasource的DDLParser将对该SQL进行Parse
5、Parse后封装成为一个CreateTableUsing类的对象。该类是一个RunnableCommand,其run方法会直接执行创建表语句。
6、该类会通过反射来创建一个org.apache.spark.sql.sources.RelationProvider,该trait定义要createRelation,如json,则创建JSONRelation,若avro,则创建AvroRelation。
7、得到external releation后,直接调用SQLContext的baseRelationToSchemaRDD转换为SchemaRDD
8、最后registerTempTable(tableName) 来注册为Table,可以用SQL来查询了。
 

五、External DataSource解析流程

先看图,图如下:
 
Spark SQL解析SQL流程如下:
1、Analyzer通过Rule解析,将UnresolvedRelation解析为JsonRelation。
2、通过Parse,Analyzer,Optimizer最后得到JSONRelation(file:///path/to/shengli.json,1.0)  
3、通过sources下得DataSourceStrategy将LogicalPlan映射到物理计划PhysicalRDD。
4、PhysicalRDD里包含了如何查询外部数据的规则,可以调用execute()方法来执行Spark查询。
 

六、External Datasource Interfaces

在第一节我已经介绍过,主要的interfaces,主要看一下BaseRelation和RelationProvider。
如果我们要实现一个外部数据源,比如avro数据源,支持spark sql操作avro file。那么久必须定义AvroRelation来继承BaseRelation。同时也要实现一个RelationProvider。
 
BaseRelation:
是外部数据源的抽象,里面存放了schema的映射,和如何scan数据的规则。
  1. abstract class BaseRelation {
  2. def sqlContext: SQLContext
  3. def schema: StructType
  4. }
  1. abstract class PrunedFilteredScan extends BaseRelation {
  2. def buildScan(requiredColumns: Array[String], filters: Array[Filter]): RDD[Row]
  3. }
1、schema我们如果自定义Relation,必须重写schema,就是我们必须描述对于外部数据源的Schema。
2、buildScan我们定义如何查询外部数据源,提供了4种Scan的策略,对应4种BaseRelation。
 
 
我们支持4种BaseRelation,分为TableScan, PrunedScan,PrunedFilterScan,CatalystScan。
   1、TableScan
          默认的Scan策略。
   2、PrunedScan
          这里可以传入指定的列,requiredColumns,列裁剪,不需要的列不会从外部数据源加载。
   3、PrunedFilterScan
          在列裁剪的基础上,并且加入Filter机制,在加载数据也的时候就进行过滤,而不是在客户端请求返回时做Filter。
   4、CatalystScan
           Catalyst的支持传入expressions来进行Scan。支持列裁剪和Filter。
 
RelationProvider:
我们要实现这个,接受Parse后传入的参数,来生成对应的External Relation,就是一个反射生产外部数据源Relation的接口。
  1. trait RelationProvider {
  2. /**
  3. * Returns a new base relation with the given parameters.
  4. * Note: the parameters' keywords are case insensitive and this insensitivity is enforced
  5. * by the Map that is passed to the function.
  6. */
  7. def createRelation(sqlContext: SQLContext, parameters: Map[String, String]): BaseRelation
  8. }

七、External Datasource定义示例

在Spark1.2之后,json和parquet也改为通过实现External API来进行外部数据源查询的。
下面以json的外部数据源定义为示例,说明是如何实现的:
 
1、JsonRelation
 
定义处理对于json文件的,schema和Scan策略,均基于JsonRDD,细节可以自行阅读JsonRDD。
  1. private[sql] case class JSONRelation(fileName: String, samplingRatio: Double)(
  2. @transient val sqlContext: SQLContext)
  3. extends TableScan {
  4.  
  5. private def baseRDD = sqlContext.sparkContext.textFile(fileName) //读取json file
  6.  
  7. override val schema =
  8. JsonRDD.inferSchema( // jsonRDD的inferSchema方法,能自动识别json的schema,和类型type。
  9. baseRDD,
  10. samplingRatio,
  11. sqlContext.columnNameOfCorruptRecord)
  12.  
  13. override def buildScan() =
  14. JsonRDD.jsonStringToRow(baseRDD, schema, sqlContext.columnNameOfCorruptRecord) //这里还是JsonRDD,调用jsonStringToRow查询返回Row
  15. }
2、DefaultSource
parameters中可以获取到options中传入的path等自定义参数。
这里接受传入的参数,来构造JsonRelation。
  1. private[sql] class DefaultSource extends RelationProvider {
  2. /** Returns a new base relation with the given parameters. */
  3. override def createRelation(
  4. sqlContext: SQLContext,
  5. parameters: Map[String, String]): BaseRelation = {
  6. val fileName = parameters.getOrElse("path", sys.error("Option 'path' not specified"))
  7. val samplingRatio = parameters.get("samplingRatio").map(_.toDouble).getOrElse(1.0)
  8.  
  9. JSONRelation(fileName, samplingRatio)(sqlContext)
  10. }
  11. }

  

八、总结
  External DataSource源码分析下来,可以总结为3部分。
  1、外部数据源的注册流程
  2、外部数据源Table查询的计划解析流程
  3、如何自定义一个外部数据源,重写BaseRelation定义外部数据源的schema和scan的规则。定义RelationProvider,如何生成外部数据源Relation。
  
  External Datasource此部分API还有可能在后续的build中改动,目前只是涉及到了查询,关于其它的操作还未涉及。
——EOF——
 

原创文章,转载请注明:

转载自:OopsOutOfMemory盛利的Blog,作者: OopsOutOfMemory

本文链接地址:http://blog.csdn.net/oopsoom/article/details/42064075

注:本文基于署名-非商业性使用-禁止演绎 2.5 中国大陆(CC BY-NC-ND 2.5 CN)协议,欢迎转载、转发和评论,但是请保留本文作者署名和文章链接。如若需要用于商业目的或者与授权方面的协商,请联系我。

 

【转载】Spark SQL之External DataSource外部数据源的更多相关文章

  1. Spark SQL之External DataSource外部数据源(二)源代码分析

    上周Spark1.2刚公布,周末在家没事,把这个特性给了解一下,顺便分析下源代码,看一看这个特性是怎样设计及实现的. /** Spark SQL源代码分析系列文章*/ (Ps: External Da ...

  2. 第十一篇:Spark SQL 源码分析之 External DataSource外部数据源

    上周Spark1.2刚发布,周末在家没事,把这个特性给了解一下,顺便分析下源码,看一看这个特性是如何设计及实现的. /** Spark SQL源码分析系列文章*/ (Ps: External Data ...

  3. 大数据技术之_19_Spark学习_03_Spark SQL 应用解析 + Spark SQL 概述、解析 、数据源、实战 + 执行 Spark SQL 查询 + JDBC/ODBC 服务器

    第1章 Spark SQL 概述1.1 什么是 Spark SQL1.2 RDD vs DataFrames vs DataSet1.2.1 RDD1.2.2 DataFrame1.2.3 DataS ...

  4. Spark SQL 之自定义删除外部表

    前言 Spark SQL 在删除外部表时,本不能删除外部表的数据的.本篇文章主要介绍如何修改Spark SQL 源码实现在删除外部表的时候,可以带额外选项来删除外部表的数据. 本文的环境是我一直使用的 ...

  5. Spark SQL External DataSource简介

    随着Spark1.2的发布,Spark SQL开始正式支持外部数据源.这使得Spark SQL支持了更多的类型数据源,如json, parquet, avro, csv格式.只要我们愿意,我们可以开发 ...

  6. Spark SQL 源代码分析系列

    从决定写Spark SQL文章的源代码分析,到现在一个月的时间,一个又一个几乎相同的结束很快,在这里也做了一个综合指数,方便阅读,下面是读取顺序 :) 第一章 Spark SQL源代码分析之核心流程 ...

  7. 【Spark SQL 源码分析系列文章】

    从决定写Spark SQL源码分析的文章,到现在一个月的时间里,陆陆续续差不多快完成了,这里也做一个整合和索引,方便大家阅读,这里给出阅读顺序 :) 第一篇 Spark SQL源码分析之核心流程 第二 ...

  8. Spark SQL 官方文档-中文翻译

    Spark SQL 官方文档-中文翻译 Spark版本:Spark 1.5.2 转载请注明出处:http://www.cnblogs.com/BYRans/ 1 概述(Overview) 2 Data ...

  9. Spark SQL 之 Data Sources

    #Spark SQL 之 Data Sources 转载请注明出处:http://www.cnblogs.com/BYRans/ 数据源(Data Source) Spark SQL的DataFram ...

随机推荐

  1. HW6.7

    public class Solution { public static void main(String[] args) { int[] count = new int[10]; int numb ...

  2. Spring MVC返回JSON数据

    将一个对象以json数据格式返回前台: @ResponseBody public  User login(User user) { return user; } 在控制器上使用@ResponseBod ...

  3. Java网络编程(URL&URLConnection)

    package cn.itcast.net.p2.ie_server; import java.io.IOException; import java.io.InputStream; import j ...

  4. hdoj 2045 不容易系列之(3)—— LELE的RPG难题

    不容易系列之(3)—— LELE的RPG难题 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/O ...

  5. CALayer精讲

    前言 CALayer包含在QuartzCore框架中,这是一个跨平台的框架,既可以用在iOS中又可以用在Mac OS X中.后面要学Core Animation就应该先学好Layer(层). 我们看一 ...

  6. IsPostBack

    1.IsPostBack介绍Page.IsPostBack是一个标志:当前请求是否第一次打开. 调用方法为:Page.IsPostBack或者IsPostBack或者this.IsPostBack或者 ...

  7. 【Stage3D学习笔记续】山寨Starling(十):高效游戏设计、纹理集和ATF

    我发布了经过批处理优化的v0.3版,点击下载:https://github.com/hammerc/hammerc-study-Stage3D/archive/v0.3.zip 先看看我们批处理优化后 ...

  8. CentOS6.5安装nginx及负载均衡配置

    所有的安装包可以去以下地址下载,或者自行去官网下载,下面都有介绍. 所有安装包地址:http://download.csdn.net/detail/carboncomputer/9238037 原文地 ...

  9. Ms SQL Server 约束和规则

    一.SQL约束 约束定义关于列中允许值的规则,是强制完整性的标准机制. 使用约束优先于使用触发器.规则和默认值.查询优化器也使用约束定义生成高性能的查询执行计划. 1:类型 约束的类型一共分三种 域约 ...

  10. 【转】Netty那点事(一)概述

    [原文https://github.com/code4craft/netty-learning/blob/master/posts/ch1-overview.md#%E5%90%88%E5%BC%80 ...