【Spark】DataSource API

什么是Spark Datasource API

Spark Datasource API 是一套連接外部數據源和Spark引擎的框架
它主要是給Spark框架提供一種快速讀取外界數據的能力,它可以方便地把不同的數據格式通過DataSource API注冊成Spark的表,然后通過Spark SQL直接讀取。它可以充分利用Spark分布式的優點進行并發讀取,而且SparkSQL本身有一個很好的Catalyst優化引擎,能夠極大的加快任務的執行。
Spark Datasource API 同時提供了一套優化機制,如將列剪枝和過濾操作下推至數據源側,減少數據讀取數量,提高數據處理效率。

我們先看一下Spark DataSource API 典型的工作方式:

sparkSession //SparkSession
      .read 
      .format("csv") //驅動類,類似JDBC的driver class
      .option(Map(....)) //你需要額外傳遞給驅動類的參數
      .load("hdfs:///...") //數據文件路徑

或者

CREATE TEMPORARY TABLE table_test USING druid OPTIONS (timeColumn "time",datasource "spark-druid-test ",zkHost "...")
CREATE TABLE spark_druid_test USING druid OPTIONS 
(timeColumn "time",
datasource "spark-demo",
zkHost "node1:2015,node2:2015,node3:2015")

目前Spark DataSource的來源主要有三個:

  • Spark 原生支持的DataSource,如常用的csv,orc,parquet;
  • Spark Packages 網站中納入的第三方包;
  • 隨其他項目一起發布的內嵌DataSource,如ES-Hadoop等。

Spark Datasource API 工作流程

讀流程
sparkSession.read 返回DataFrameReader,它是DataSource 讀數據的入口,DataFrameReader中提供了讀取多種Spark內置DataSource 的方法,包括參數配置接口。

sparkSession通過調用不同DataSource的接口,實現DataSource的參數配置,最終通過load方法真正建立Spark與DataSource的連接。

load函數最重要的功能就是將baseRelation轉換成DataFrame,該功能是通過sparkSession的
def baseRelationToDataFrame(baseRelation: BaseRelation): DataFrame
接口實現的,其中的參數baseRelation通過DataSource類的resolveRelation方法提供。

resolveRelation中使用反射創建出對應DataSource實例,協同用戶指定的userSpecifiedSchema進行匹配,匹配成功返回對應的baseRelation:

  • 如果是基于文件的,返回HadoopFsRelation實例
  • 非文件的,返回如KafkaRelation或者JDBCRelation

上面提到baseRelationToDataFrame接受baseRelation參數返回DataFrame,是通過Dataset.ofRows(sparkSession,logicalPlan)方法實現的,其中的參數logicPlan是由LogicalRelation(baseRelation)得到。

寫流程
dataSet.write 返回DataFrameWriter類型對象, 它是DataSource寫數據的入口,與讀機制類似,DataFrameWriter提供了DataSource的接口和參數配置方法,底層落到save方法上,運行runCommand執行寫入過程,runCommand所需的LogicalPlan由對應的DataSource.planForWriting()提供。

如何擴展Spark Datasource API

所有DataSource的擴展都是基于spark\sql\core\src\main\scala\org\apache\spark\sql\sources\interfaces.scala提供的接口來實現:

  • interface

一般來講,自定義數據源需要實現以下接口和功能:

  • BaseRelation:代表了一個抽象的數據源,描述了數據源和Spark SQL交互
  • 數據掃描接口(根據需要實現):
    • TableScan:全表數據掃描
    • PrunedScan:返回指定列數據,其他的列數據源不用返回
    • PrunedFilteredScan:指定列的同時,附加一些過濾條件,只返回滿足過濾條件的數據
  • RelationProvider: 根據用戶提供的參數返回一個BaseRelation
  • 數據源RDD: 將DataSource的數據讀取后裝配成RDD

以JDBC為例看一下DataSource擴展的流程:

(1) 【JDBCRelation】

  • JDBCRelation

JDBCRelation實現了BaseRelation、PrunedFilteredScan和InsertableRelation接口,在Spark層面描述了JDBC DataSource,以及數據讀取(buildScan)和寫入(insert)邏輯,它的全部工作就是重寫以上三個接口的方法,方法清單:

  • BaseRelation:sqlContext、needConversion、schema、unhandledFilters

  • PrunedFilteredScan:上文提到了,這個是提供列裁剪和過濾的讀取接口,只需要實現一個方法buildScan就好了,buildScan通過調用JDBCRDD.scanTable將從數據庫中讀出的數據裝配成RDD。

  • InsertableRelation:實現寫入接口insert,將DataFrame寫入DataSource,調用的是DataFrameWriter的jdbc方法。

  • buildScan & insert

(2) 【JdbcRelationProvider】

  • JdbcRelationProvider

JdbcRelationProvider實現了CreatableRelationProvider、RelationProvider、DataSourceRegister。重寫了shortName和兩個createRelation方法:

  • DataSourceRegister:shortName方法比較簡單,就是為DataSource提供一個別名,這樣用戶在使用實現的DataSource API的時候,提供這個別名就可以了。
  • RelationProvider:重寫createRelation方法,根據用戶提供的參數創建baseRelation
  • CreatableRelationProvider:重寫createRelation方法,基于給定的DataFrame和用戶參數返回baseRelation,它描述了當數據已存在情況下的createRelation行為。支持寫入模式如append、overwrite。

(3) 【JDBCRDD】
一個JDBCRDD代表了關系數據庫中的一張表,在Spark的Driver和Executor端都必須能夠通過JDBC訪問這張表,其中Driver獲取schema信息,Executor獲取數據。
JDBCRDD重寫了RDD的getPartitions和compute方法,其中compute方法就是從關系表里讀出數據,使用JdbcUtils.resultSetToSparkInternalRows( )將數據轉換成SparkInternalRow格式。
JDBCRDD的伴生類中還有兩個非常重要的方法:resolveTable和scanTable。這兩個方法功能都比較清楚,前者是將表的schema信息以Spark 內部StructType的形式返回,后者其實是使用對應的參數創建了一個JDBCRDD的對象,對象中以RDD[InternalRow]形式映射了當前讀取的關系表數據。這兩個方法分別被JDBCRelation中重寫的方法-schemabuildScan調用。

DataSource API 流程調用鏈

下圖是DataSource的讀寫流程調用鏈,以JDBC為例:

  • read
  • write

File Source 實現原理初探

Spark中內置的基于文件的數據源有以下幾種:

  • text
  • csv
  • json
  • parquet
  • orc

它們都擴展了DataSource中的FileFormat特質,那么什么是FileFormat呢?

FileFormat有讀、寫兩方面的功能:

  • 讀:將文件中的數據讀取成為Spark內部的InternalRow格式
  • 寫:將Spark內部的InternalRow格式以對應的格式寫入文件

該特質有幾個主要的接口:

  • inferSchema(自動推測模式),返回類型為Option[StructType]
    當option中設置inferSchema為true情況下,無需用戶編碼顯示指定模式,而是由系統自動推斷模式。但是當該文件格式不支持模式推測或者傳入的文件路徑非法時,該方法返回None,此時需要用戶顯示指定schema。基本思路就是將傳入的文件路徑使用baseRelationToDataFrame方法轉換成為DataFrame,然后取一行進行格式推測。
  • prepareWrite,返回類型OutputWriterFactory:
    這里通過參數spark.sql.sources.outputCommitterClass可以配置用戶自定義的output committer。
  • supportBatch,是否支持批量列的讀入和寫出
  • isSplitable,單個文件是否能被切分
  • buildReader,返回一個能夠將單個文件讀成Iterator[InternalRow]的方法

DataSource 在匹配類型時,會通過反射得到DataSource類型(FileFormat),返回HadoopFsRelation的BaseRelation,后續通過DataFrameReader的load接口獲取DataFrame。

寫過程調用DataFrameWriter save方法,構造DataSource實例,通過className確定DataSource類型,然后調用dataSource.write(mode, df)寫操作。

接下來看一下datasource的寫方法:write方法通過providingClass.newInstance()實例化分別匹配CreatableRelationProvider和FileFormat,如果是CreatableRelationProvider(其實針對內置的datasource,就是JDBCRelationProvider),調用dataSource.createRelation,如果是FileFormat類型,構造InsertIntoHadoopFsRelationCommand,其實該實例是一個RunnableCommand,得到該實例后,就通過調用sparkSession.sessionState.executePlan(plan).toRdd來執行RunnableCommand

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,316評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,481評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,241評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,939評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,697評論 6 409
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,182評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,247評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,406評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,933評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,772評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,973評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,516評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,209評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,638評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,866評論 1 285
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,644評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,953評論 2 373