[Spark2.0]Spark SQL, DataFrames 和Datasets指南

綜述

        Spark SQLSpark提供的針對結構化數據處理的模塊。不同於基本的Spark RDD APISparkSQL提供的接口提供了更多的關於數據和計算執行的信息。在內部,SparkSQL使用這些額外信息完成額外的優化。這裏有幾種方式可以和SparkSQL相互操作,包括SQLDataset API。計算結果的時候使用相同的執行

        本頁中所有示例使用到的樣例的數據都包含在Spark發佈中,而且都能在spark-shellpyspark或者sparkR中運行。

 

SQL

        Spark SQL的一種用法是執行SQL查詢。Spark SQL也可以用於從已安裝的Hive中讀取數據。更多的關於此特性的配置,請參考Hive Tables。當從內部其他編程語言執行SQL,結果將以Dataset/DataFrame形式返回。你也可以通過command-line或者JDBC/ODBCSQL接口進行交互。

 

DatasetsDataFrames

        Dataset是分佈式數據集合。DatasetSpark1.6新增的接口,用以提供RDDs(強類型,有使用強大的lambda函數的能力)的優點和Spark SQL的經優化的執行引擎的優點。Dataset可以從JVM對象進行構造並通過轉換函數(如mapflatmapfilter等)進行操作。DatasetAPI支持ScalaJavaPython不支持Dataset API。但因爲Python本身的動態性,DatasetAPI的許多優點都已經可用(比如,你可以通過名字很自然的訪問一行的某一個字段,如row.columnName),R的情況與此類似。

        DataFrameDataset組織成命名列的形式。它在概念上相當於關係型數據庫中的表,或者R/Python中的數據幀,但是在底層進行了更多的優化。DataFrames可以從多種數據源創建,例如:結構化數據文件、Hive中的表、外部數據庫或者已存在的RDDsDataFrame API支持ScalaJavaPythonR。在ScalaJavaDataFrame其實是DatasetRowS的形式的表示。在Scala API中,DataFrame僅僅是Dataset[Row]的別名。但在Java中,使用者需要使用Dataset<Row>來表示一個DataFrame

       在本文檔中,我們會經常將Scala/Java DatasetRowS作爲DataFrame的參考。

 

 

開始使用

起始點:SparkSession

       Spark中所有功能的切入點是SparkSession類。直接使用SparkSession.builder()就可以創建一個基本的SaprkSession


       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"找到完整的代碼。

        SparkSessionSpark2.0開始提供的內建了對Hive特性的支持,包括使用HiveQL寫查詢語句、調用Hive UDFs、從Hive表讀取數據的能力。你不需要事先部署Hive就能使用這些特性。

 

創建DataFrame

       使用SparkSession,應用可以從已存在的RDDHive表或者Spark數據源創建DataFrame。下面的示例從一個JSON文件創建一個DataFrame:


       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"找到完整的代碼。

 

無類型的Dataset操作(aka DataFrame Operations

        DataFrameScalaJavaPythonR中爲結構化數據操作提供了一個特定領域語言支持。

       就像網文提到的,在Spark2.0中,在ScalaJavaAPI中,DataFrame僅僅是DatasetRowS表示。與Scala/Java中的強類型的“帶類型轉換操作”相比,這些操作也可以看做“無類型轉換操作”。

這裏我們提供了一些使用Dataset進行結構化數據處理的基本示例:

計算機生成了可選文字:|0// This import is needed to use the S-notation import spark. implicits._ print the schema in a tree format df. pri ntschema() // root /-- age: long (nu77ab7e = true) / -- name: string (nu77ab7e = true) select only the "name" column df. sel . show() / name / 'Michae 7 / / Andy/ / Justin/ select everybody, but increment the age by 1 df. S"age" + 1). show() / name / (age + 1) / 'Michae 7 / / Andy/ / Justin/ nun,' 31 / 20/ select people older than 21 > 21). show() / age / name / / 30 'Andy/ Count people by age df. . count() . show() / age / count / / 19/ // 'nun,' 1/ 1/ 1/

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"找到完整的代碼。

       可以在Dataset上執行的操作的類型的完整列表可以參考API文檔

       除了簡單的列引用和表達式外,Dataset同時有豐富的函數庫,包括字符串操作、日期算法、常用數學操作等。完整的列表可參考DataFrame Function Reference

 

編程執行SQL查詢語句

       Sparksession中的sql函數使得應用可以編程式執行SQL查詢語句並且已DataFrame形式返回:

計算機生成了可選文字:|0// Register the DataFrame as a SQL temporary view df. createOrRep1 aceTempVi "peopl e val sq 1 DF spark.sql ("SELECT * FROM people") sq 1 DF . show() / age / name / /nul /Michae / // / 30/ Andy/ / 19/ Justin/

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"找到完整的代碼。

 

創建Dataset

        DatasetRDD很像,不同的是它並不使用Java序列化或者Kryo,而是使用特殊的編碼器來爲網絡間的處理或傳輸的對象進行序列化。對轉換一個對象爲字節的過程來說編碼器和標準系列化器都是可靠的,編碼器的代碼是自動生成並且使用了一種格式,這種格式允許Spark在不需要將字節解碼成對象的情況下執行很多操作,如filteringsortinghashing等。

計算機生成了可選文字:|0// Note: Case classes in Scala 2.10 can support only up to 22 fields. k around this limit, // you can use custom classes that implement the product interface case class person(name: String, age: Long) // Encoders are created for case classes val casecl assDS — Seq(person("Andy", 32)) . toDS() casecl assDS . show() // /name/age/ // /Andy/ 32/ To wor // Encoders for most common types are automatically provided by importing sp ark. implicits._ val primitiveDS Seq(l, 2, 3) . toDS() primitiveDS .map(_ + 1) .collect ( ) // Returns: Array(2, 3, 4) // DataFrames can be converted to a Dataset by providing a class. 77 be done by name val path 'exampl es/s rc/mai n/ resources/people . json " val peopl eDS spark . read . json(path) . as [person] peopl eDS . show() / age / name / /nul /Michae / // / 30/ Andy/ / 19/ Justin/ Mapp ing wi

可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"找到完整的代碼。

 

RDD互操作

        Spark SQL支持兩種將已存在的RDD轉化爲Dataset的方法。第一種方法使用反射推斷包含特定類型對象的RDD的結構。這種基於反射的方法代碼更加簡潔,並且當你在寫Spark程序的時候已經知道RDD的結構的情況下效果很好。

       第二種創建Dataset的方法是通過編程接口建立一個結構,然後將它應用於一個存在的RDD。雖然這種方法更加繁瑣,但它允許你在運行之前不知道其中的列和對應的類型的情況下構建Dataset

 

使用反射推斷結構

        Spark SQLScala接口支持自動的將一個包含case classRDD轉換爲DataFrame。這個case class定義了表結構。Caseclass的參數名是通過反射機制讀取,然後變成列名。Caseclass可以嵌套或者包含像SeqArray之類的複雜類型。這個RDD可以隱式的轉換爲一個DataFrame,然後被註冊爲一張表。這個表可以隨後被SQLstatement使用。

計算機生成了可選文字:|0i mport i mport // For i mport org. apache. spark. sq] . catalyst. encoders. Expressi onEncoder org. apache. spark. sq] . Encoder imp 7 i cit conversions from RDDs to DataFrames spark. impl icits._ // Create an ROD of Person objects from a text file, convert it to a Dataframe val peopl eDF spark. sparkcontext . textFi 1 es/src/mai n/resources/peopl e. txt") . map( split(', . map(attributes attributes (1). trim. tolnt)) // Register the DataFrame as a temporary view peopl eDF. createorRep1 aceTempvi e") // SQL statements can be run by using the sq 7 methods provided by spark val teenagersDF spark. sql ("SELECT name, age FROM people WHERE age BETWEEN 13 AND 19") // The columns of a row in the result can be accessed by field index teenagersDF. map (teenager / Name : va rue / Just in / or by field name teenagersDF. map (teenager 'Name : 'Name : + teenager (O)). show() + teenager. getAs [stri ng] ("name")). show() / Name : va rue / Just in / No pre-defined encoders for Dataset [Map[K, define explicitly implicit val mapEncoder — org. apache. spark. sq 1 . Encoders. kryoCMap [stri ng, Any]] // primitive types and case c7asses can be also defined as implicit val stri ng1ntMapEncoder: Encoder [Map C string, Int]] = Expressi onEncoder() // row. getva7uesMap[T] retrieves mu7tip7e columns at once into a Map[string, T] teenagersDF. map (teenager teenager. getva1uesMap [Any] (List("name", "age")) ) . col lect() // "Justin", "age " 19))

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"in the Spark repo. 找到完整的代碼。

 

以編程方式指定模式

       case class不能被事先定義(比如記錄的結構被編碼爲字符串,或者對不同的用戶,文本數據集被不同的解析並進行字段投影),DataFrame可以通過以下3個步驟實現編程創建:

  1. 從原始RDD創建RowS形式的RDD
  2. StructType創建匹配步驟1RowS形式的RDD的模式
  3. 通過SparkSession提供的createDataFrame方法將模式應用於RowS形式的RDD

 

例如:

計算機生成了可選文字:|0import org.apache.spark.sq] . types. _ // Create an RDD val peopleRDD = spark.sparkcontext. txt") // The schema is encoded in a string val schemastring 'name age' / Generate the schema based on the string of schema val fields = schemastring.split(" ' .map(fie1dName StructFie1d(fie1dName, StringType, val schema = StructType(fie1ds) / Convert records of the RDD (people) to Rows null able schema — true)) val rowRDD = peopl eRDD .map(_. split(" , ")) .map(attributes , / Apply the schema to the RDD attributes(l) . trim)) s chemaJüR *RowSh$jtä5RDD , DataFrame al peopl eDF spark. createDataFrame(rowRDD, schema) // Creates a temporary view using the DataFrame peopl eDF . createOrRep1 aceTempVi ew( "peopl e // SQL can be run over a temporary view created using DataFrames val results spark.sql ("SELECT name FROM people") // The results of SQL queries are DataFrames and support all the normal RDD operations // The columns of a row in the result can be accessed by field index or by field name results .map(attributes "Name: + attributes(O)) .show() va lue / /Name: Michael/ / Name: Andy/ / Name: Justin/

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SparkSQLExample.scala"找到完整的代碼。

 

 

數據源

        Spark SQL通過DataFrame接口,可以支持對多種數據源的操作。DataFrame可以使用關係轉換來進行操作,而且可以用來創建臨時視圖。將DataFrame註冊爲臨時視圖可以允許你在數據上運行SQL查詢語句。本節講解使用SparkData Source加載數據和保存數據的通用方法,然後

詳細講述內部支持的數據源可用的特定操作。

 

通用Load/Save函數

       最簡單的,默認的數據源(parquet,除非使用spark.sql.sources.default進行了配置)將被用於所有的操作。

計算機生成了可選文字:|0val usersDF spark . read . 1 oad("exampl es/src/mai n/ resources/users . parquet") usersDF . sel ect("name " "favori te_col or ") . wri te . save ("namesAndFa«01 ors . parqu et")

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SQLDataSourceExample.scala"找到完整的代碼。

 

手動指定選項

       你可以手動指定數據源以及數據源附帶的額外選項。數據源被他們的完全限定名來指定(如,org.apache.spark.sql.parquet),但對於內部支持的數據源,你可以使用短名(jsonparquetjdbc)。DataFrame可以使用這種語法從任何可以轉換爲其他類型的數據源加載數據。

計算機生成了可選文字:|0val peopl eDF spark . read . format("json ") . 1 oad("exampl es/src/mai n/ resources/p eople.json ") peopl eDF . select("name", "age") .wri te. format("parquet") . . pa r quet")

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SQLDataSourceExample.scala"找到完整的代碼。

 

在文件上直接執行SQL

       除了使用讀取API加載一個文件到SATAFrame然後查詢它的方式,你同樣可以通過SQL直接查詢文件。

計算機生成了可選文字:|0val sq 1 DF spark.sql ("SELECT ers . parquet' ") FROM parquet. •examples/src/main/resources/us

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SQLDataSourceExample.scala"找到完整的代碼。

 

保存模式

       保存操作可選SaveMode,它指定了如何處理現有的數據。需要重視的一點是這些保存模式沒有使用任何的鎖,並且不具有原子性。此外,當執行Overwrite時,數據將先被刪除,然後寫出新數據。

Scala/Java

其他語言

含義

SaveMode.ErrorIfEcists(默認)

error”(默認)

保存DataFrame到數據源時,如果數據已經存在,將拋出一個異常。

SaveMode.Append

append

保存DataFrame到數據源時,如果數據/表存在時,DataFrame的內容將追加到已存在的數據後。

SaveMode.Overwrite

overwrite

Overwrite模式意味着當保存一個DataFrame到數據源時,如果數據/表已經存在,存在的數據將會被DataFrame的內容覆蓋。

SaveMode.Ignore

ignore

Ignore模式意味着當保存一個DataFrame到數據源時,如果數據已經存在,保存操作將不會保存DataFrame的內容,並且不會改變原數據。這與SQL中的CREATE TABLE IF NOT EXISTS相似。

 

保存到持久化表

       也可以通過saveAsTable命令將DataFrame作爲持久化表保存到Hive元數據庫中。注意使用此特性時不需要事先部署HiveSpark將爲你創建一個默認的本地Hive元數據庫(使用Derby)。不同於createOrReplaceTempView命令,saveAsTable將具體化DataFrame的內容並且在Hive元數據庫中創建一個指向數據的指針。在你保持你的連接是到相同的元數據庫時,當你的Spark程序重啓後持久化表依然會存在。通過在SparkSession上使用表名調用table命令,可以創建用於持久化表的DataFrame

       默認的saveAsTable將會創建一個託管表,意味着數據的位置醬油元數據庫控制。託管表也有他們自己的數據,當對應的表被刪除時這些數據會一併刪除。

 

Parquet文件

       Parquet是一種被很多其他數據處理系統支持的列式文件。Spark SQL提供了可以自動保存原始數據模式的對Parquet文件讀取和寫入的操作。當寫入一個Parquet文件時,因爲兼容性原因,所有的列都會自動轉換爲nullable(可爲空的)。

 

編程式加載數據

       使用上面例子的數據:

計算機生成了可選文字:|0// Encoders for most common types are automatically provided by importing sp ark. implicits._ import spark. implicits._ val peopl eDF spark . read . json ("exampl es/s rc/mai n/ resources/peopl e . json ") // DataFrames can be saved as Parquet files, maintaining the schema informat on peopl eDF . wri te . . parquet") // Read in the parquet file created above // Parquet files are self-describing so the schema is preserved // The result of loading a Parquet file is also a DataFrame val parquetFi1eDF spark . read . . parquet") // Parquet files can also be used to create a temporary view and then used i n SQL statements parquetFi 1 eDF . createOrRep1 aceTempVi "parquetFi 1 e ") val names DF name FROM parquetFi1e WHERE age BETWEEN 13 A ND 19") namesDF .map(attributes "Name: + attributes(O)) .show() / Name : va lue / Justin/

       可以從Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SQLDataSourceExample.scala"找到完整的代碼。

 

分區發現

       表分區是Hive等系統中常用的優化方法。在一個分區表中,數據常常存放在不同的目錄中,根據分區列的值的不同,編碼了每個分區目錄不同的路徑。目前parquet數據源已經可以自動的發現和推斷分區信息。例如,我們可以用下面的目錄結構存儲所有我們以前經常使用的數據到分區表,只需要額外的添加兩個列gendercountry作爲分區列:

計算機生成了可選文字:|0path to table gender—male country—US data. parquet country—CN data. parquet gender—female country—US data. parquet country—CN data. parquet

       使用SparkSession.read.parquet或者SparkSession.read.load加載path/to/table後,Spark SQL能夠自動的從路徑中提取分區信息。返回的DataFrame的模式結構是:

計算機生成了可選文字:|0root I-- name: string (nullable — true) age: long (nullable — true) gender: string (nullable — true) country: string (nullable — true)

       注意分區列的數據類型是自動推斷的。目前支持數值型數據和字符串型數據。有時候用戶並不想自動推斷分區列的數據類型,這種情況下,可以通過配置spark.sql.sources.partitionColumnTypeInference.enabled這個參數來配置自動類型推斷,默認情況下是true。當關閉類型推斷後,分區列的類型將爲字符串型。

       Spark1.6.0開始,在默認情況下,只在給定的路徑下進行分區發現。在上述的例子中,如果用戶將path/to/table/gender=male傳給SparkSession.read.parquet或者SparkSession.read.loadgender將會被認爲是分區列。如果用戶需要指定分區發現開始的基礎路徑,可以將basePath設置到數據源選項。例如,當path/to/table/gender=male是數據的路徑,並且用戶設置basePathpath/to/tablegender將作爲分區列。

 

模式(schema)合併

       ProtocolBufferAvro,和Thrift類似,Parquet同樣支持schema的演變。用戶可以以一個簡單點的schema開始,然後在需要時逐漸的添加更多列到schema。使用這種方法,用戶將最終得到由不同的但是相互兼容的schema構成的多個Parquet文件。Parquet數據源目前可以自動的檢測這種情況並且合併這些文件的schema

       由於合併schema是相對代價較大的操作,而且在大多數情況下並不需要這樣,從1.5.0開始我們默認將它關閉,你可以通過以下方法使它生效:

  1. 在讀取Parquet文件時(就像下面的例子)設置數據源操作mergeSchematrue
  2. 設置全局SQL選項spark.sql.parquet.mergeSchematrue
計算機生成了可選文字:|0// This is used to implicitly convert an RDD to a DataFrame. import spark. implicits._ // Create a simple DataFrame, store into a partition directory val squaresDF spark. sparkContext.makeRDD(1 to 5) .map(i (i , ) . "square") spark. read . opti "true") . parquet("data/test_table") squaresDF . wri te . parquet( "data/test_tabl e/key=l // Create another DataFrame in a new partition directory, // adding a new column and dropping an existing column val cubesDF spark. sparkContext.makeRDD(6 to 10) .map(i cubesDF . wri te . parquet( " data/test_tabl e/key=2 ") // Read the partitioned table val mergedDF mergedDF . pri ntschema() ) . toDF ("val ue" , "cube // The final schema consists of all 3 columns in the Parquet files together // with the partitioning column appeared in the partition directory paths root - value: int (nullable — true) square: int (nullable — true) cube: int (nullable — true) - key : int (nullable true)

可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SQLDataSourceExample.scala"找到完整的代碼

 

Hive原數據

       當讀寫Hive元存儲Parquet表時,爲了更好的性能,SparkSQL將試圖使用它自己支持的Parquet代替Hive SerDe。這種行爲可以通過spark.sql.hive.convertMetastoreParquet進行配置,默認已經開啓。

 

Hive/Parquet schema調節

       從表的schema處理的角度來看,HiveParquet有兩點關鍵的不同之處。

  1. Hive是類型敏感的,而Parquet並不是
  2. Hive中所有列都是非空的,而Parquet中非空是很重要的特性。

       

       因爲這個原因,當我們需要將Hive元存儲轉換爲Spark SQL Parquet表中的Parquet表時,我們需要調節Hive元存儲的schemaParquetschema。調節規則如下:

  1. 不管是否可爲空值,兩種schema中具有相同名字的字段必須具有相同的數據類型。這種調節字段應該有與Parquet一方相同的數據類型,因此可爲空值的特性很重要。
  2. 調節的schema準確的包含在Hive元存儲schema中定義的字段。
    • 任何只在Parquet schema中出現的字段都會在調節schema中被丟棄
    • 任何只出現在Hive元存儲schema中的字段都會在調節schema中被添加爲可爲空的字段

 

元數據更新

       爲了更好的性能,Spark SQL會緩存Parquet元數據。當Hive元存儲Parquet錶轉換操作可用時,這些被轉換的表的元數據同樣被緩存。如果這些表被Hive或者外部工具更新,你需要手動更新元數據以保持其一致性。

計算機生成了可選文字:|0// spark is an existing Spark-session spark . catal og . refresh Tabl e

 

配置

        Parquet的配置可以使用SparkSession中的setConf方法進行,或者使用SQL執行SET key=value命令。

計算機生成了可選文字:|0Property Name spark . sq 1 . parquet. binaryAsString spark . sq 1 . parquet. int96AsTimestamp spark . sq 1 . parquet. cacheMetadata spark . sq 1 . parquet. compressi on . codec spark . sq 1 . parquet. fi Iterpushdown spark . sq 1 . hive . convertMetastoreParquet spark . sq 1 . parquet.mergeschema Default false true true gzip true true false Meaning Some other Parquet-producing systems, in particular Impala, Hive, and older versions of Spark SQL, do not differentiate between binary data and strings when writing out the Parquet schema. This flag tells Spark SQL to interpret binary data as a string to provide compatibility with these systems. Some Parquet-producing systems, in particular Impala and Hive, store Timestamp into INT96. This flag tells Spark SQL to interpret INT96 data as a timestamp to provide compatibility with these systems. Turns on caching of Parquet schema metadata. Can speed up querying of static data. Sets the compression codec use when writing Parquet files. Acceptable values include: uncompressed, snappy, gzip, Izo. Enables Parquet filter push-down optimization when set to true. When set to false, Spark SQL will use the Hive SerDe for parquet tables instead of the built in support. When true, the Parquet data source merges schemas collected from all data files, otherwise the schema is picked from the summary file or a random data file if no summary file is available.

 

JSON數據集

        Spark SQL可以自動推斷JSON數據集的schema並且加載爲Dataset[Row]。可以對String類型的RDD或者JSON文件使用SparkSession.read.json()來實現這種轉換。

       注意這裏的JSON文件不是通常意義的JSON文件。每一行必須包含分離的,完整有效的JSON對象。因此,不支持常用的多行式JSON文件。

計算機生成了可選文字:|0// A JSON dataset is pointed to by path. // The path can be either a single text file or a directory storing text files val path 'exampl es/s rc/mai n/ resources/people . json " val peopl eDF spark. read . json(path) // The inferred schema can be visualized using the printschema() method peopl eDF . pri ntschema() // root // / -- age: long (nullable true) // / -- name: string (nullable — true) // Creates a temporary view using the DataFrame peopl eDF . createOrRep1 aceTempVi ew( "peopl e // SQL statements can be run by using the sq 7 methods provided by spark val teenagerNamesDF name FROM people WHERE age BETWEEN 13 AND 19") teenagerNamesDF . show() // / name / // /Justin/ // Alternatively, a DataFrame can be created for a JSON dataset represented by // an RDD[String] storing one JSON object per string val otherpeop1eRDD = spark. sparkContext.makeRDD( "l "name " : "Yi n" , "address " : {"ci ty" : "Col umbus " , " state val otherpeople spark . read . json (otherpeopl eRDD) otherpeople . show() address / name / / [Co lumbus , Oh i o] / Vin / " : "Ohi " " : Nil)

       注意,RDD[String]中每一個元素必須是一個字符串形式的JSON對象。

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/SQLDataSourceExample.scala"找到完整的代碼。

 

Hive

        Spark SQL同樣支持從Apache Hive中讀寫數據。但是,自從Hive有大量依賴之後,這些依賴就不包括在Spark發佈版中了。如果Hive的依賴可以在環境變量中找到,Spark將自動加載它們。注意這些Hive依賴項同樣必須在每個worker節點上存在,因爲他們需要訪問Hive序列化和反序列化庫以便可以訪問Hive中存儲的數據。

       配置可以在conf/目錄中的hive-site.xml, core-site.xml(安全配置),hdfs-site.xml(HDFS配置)這幾個文件中進行配置。

       當在Hive上工作時,必須實例化SparkSessionHive的支持,包括對持久化Hive元存儲的連通性,對Hive序列化反序列化,Hive用戶自定義函數的支持。當沒有在hive-site.xml配置是,context會自動在當前目錄創建metastore_db並且創建一個被spark.sql.warehouse.dir配置的目錄,默認在spark應用啓動的當前目錄的spark-warehouse。注意從Spark2.0.0開始hive-site.xml中的hive.metastore.warehouse.dir參數被棄用。作爲替代,使用spark.sql.warehouse.dir來指定倉庫中數據庫的位置。你可能需要授權寫權限給啓動spark應用的用戶。

計算機生成了可選文字:|0import org.apache.spark.sq].Row import org.apache.spark.sq] . Sparksession Record(key: Int, case class // warehouseLocation points value: String) to the default location for managed databases and tables val warehouseLocati on "fi 1 e : S{system: user . di r}/spark-warehouse " val spark = Sparksession . builder() . appName("Spark Hive Example") . confi . sq 1 . warehouse . di r" , . enabl eHiveSupport() . getorcreate() import spark. implicits._ import spark. sq] warehouseLocation) sql ("CREATE TABLE IF NOT EXISTS src (key INT, value STRING) sq 1 ("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kvl. txt' // Queries are expressed in HiveQL * FROM src") .show() // /key/ va / // /238/va1_238/ // / 86/ val_86/ // /311/va1_311/ INTO TABLE src")計算機生成了可選文字:|0// Aggregation queries are also supported. FROM src") .show() / count (1) / 500 / The results of SQL queries are themselves DataFrames and support all normal functions. val sq 1 DF — key, value FROM src WHERE key < 10 ORDER BY key") // The items in DaraFrames are of type Row, which allows you to access each column by ordinal. val stringsDS — sq1DF .map { case Row(key: Int, value: String) s"Key: Skey, Value: Svalue" stri ngsDS . show() / Key: / Key: / Key: O, O, O, Va lue : Va lue : Va lue : va lue / va va 7_0/ va Vou can also use DataFrames to create temporary views within a Hivecontext. val recordsDF spark. createDataFrame((1 to 100) .map(i Record(i, s"val Si "))) records DF . createOrRep1 aceTempVi " records ") // Queries can then join DataFrame data with data stored in Hive. sq 1 ("SELECT * FROM records r JOIN src s ON r.key = s.key") .show() // /key/ va lue/key/ va lue/ 2/ val_2/ 2/ val_2/ 4/ va7_4/ 2/ val_2/ 2/ val_2/ 4/ va7_4/

       可以在Spark倉庫的"examples/src/main/scala/org/apache/spark/examples/sql/hive/SparkHiveExample.scala"找到完整的代碼。

 

不同版本的Hive元存儲的交互

        Spark SQLHive支持的最重要的特點之一是與Hive元存儲的交互,這使得SparkSQL可以訪問Hive表的元數據。從Spark1.4.0開始,可以使用一個Spark SQL的二進制構建來查詢不同版本的Hive元存儲。Spark SQL在內部編譯Hive1.2.1並且使用這些classes用於內部執行(序列化反序列化,UDFsUDAFs等)

       可以使用下面的選項來配置用於檢索元數據的Hive的版本:

計算機生成了可選文字:|0Property Name spark . sq 1 . hive .metastore . versi on spark . sq 1 . hive .metastore . jars spark . sq 1 . hive . metastore . sharedprefi xes spark . sq 1 . hive . metastore . barri erprefi xes Default 1.2.1 builtin com.mysql.jdbc, org.postgresql , com.microsoft.sq1server, oracle.jdbc (empty) Meaning Version of the Hive metastore. Available options are o. 12. o through 1.2.1. Location of the jars that should be used to instantiate the HiveMetastoreClient. This property can be one of three options: . builtin Use Hive 1.2.1 , which is bundled with the Spark assembly when -Phive is enabled. When this option is chosen, spark . sq 1 . hive . metastore . versi on must be either 1.2 . 1 or not defined. 2. maven Use Hive jars of specified version downloaded from Maven repositories. This configuration is not generally recommended for production deployments. 3. A classpath in the standard format for the JVM. This classpath must include all of Hive and its dependencies, including the correct version of Hadoop. These jars only need to be present on the driver, but if you are running in yarn cluster mode then you must ensure they are packaged with you application. A comma separated list of class prefixes that should be loaded using the classloader that is shared between Spark SQL and a specific version of Hive. An example of classes that should be shared is JDBC drivers that are needed to talk to the metastore. Other classes that need to be shared are those that interact with classes that are already shared. For example, custom appenders that are used by log4j. A comma separated list of class prefixes that should explicitly be reloaded for each version of Hive that Spark SQL is communicating with. For example, Hive IJDFs that are declared in a prefix that typically would be shared (i.e. org . apache . spark . k).

 

其他數據庫的JDBC

       Spark SQL同樣包括可以使用JDBC從去他數據庫讀取數據的數據源。此功能優先使用JdbcRDD.這是因爲返回的結果作爲一個DataFrame並且可以輕鬆地使用Spark SQL處理或者與其他數據源進行連接。使用Java或者Python可以更容易的使用JDBC數據源因爲它們不需要用戶提供ClassTag。(注意這與Spark SQLJDBC服務器可以允許其他應用使用Spark SQL執行查詢語句不同)

       在開始之前你需要將你指定的數據庫的JDBC driver包含在Spark的環境變量中。例如,爲了從Spark Shell連接到postgres,你需要執行以下命令:

       

       遠程數據庫的表可以被加載爲DataFrame或者使用Data Sources API加載爲Spark SQL臨時表。支持以下選項:

計算機生成了可選文字:|0Property Name urn dbtable driver parti ti oncol umn , 1 ower80und , upper80und, numparti ti ons fetchsi ze Meaning The JDBC URL to connect to. The JDBC table that should be read. Note that anything that is valid in a FROM clause of a SQL query can be used. For example, instead of a full table you could also use a subquery in parentheses. The class name of the JDBC driver to use to connect to this URL. These options must all be specified if any of them is specified. They describe how to partition the table when reading in parallel from multiple workers. parti ti oncol umn must be a numeric column from the table in question. Notice that 1 and uppersound are just used to decide the partition stride, not for filtering the rows in table. So all rows in the table will be partitioned and returned. The JDBC fetch size, which determines how many rows to fetch per round trip. This can help performance on JDBC drivers which default to low fetch size (eg. Oracle with 10 rows).

 

計算機生成了可選文字:|0val jdbcDF Map("url " "dbtable" spark . read . . opti ons( "jdbc:postgresql :dbserver" , "schema. tablename")) . load()

 

排錯

  • JDBC driver類必須在客戶端和所有執行器上對原始類加載器可見。這是因爲JavaDriverManager類在用戶打開連接時,要進行安全檢查,檢查其中的結果已經忽略了所有的對原始類加載器不可見的部分。一個方便的方法是更改所有worker節點的compute_classpath.sh使其包含你的driverJAR
  • 一些數據庫,比如H2,要求將所有的名字轉換爲大寫,你需要在Spark SQL中使用大寫。

 

 

性能調優

       對一些工作負載,可以通過將數據緩存在內存中,在某些經驗項上進行調優來提高性能。

 

緩存數據到內存

        Spark SQL可以通過調用 spark.cacheTable("tableName")或者dataFrame.cache()來將表以列式形式緩存在內存中。然後Spark SQL可以只掃描需要的列並且可以自動調節壓縮以最小內存使用率和GC壓力。你可以調用spark.uncacheTable("tableName")來將表從內存中刪除。

       可以在SparkSession上使用setConf方法來配置內存緩存,或者使用SQL執行SET key=value命令。

計算機生成了可選文字:|0Property Name spark . sq 1 . i nMemoryC01 umnarStorage . compressed spark . sq 1 . i nMemoryC01 umnarStorage . batchsi ze Default true 10000 Meaning When set to true Spark SQL will automatically select a compression codec for each column based on statistics of the data. Controls the size of batches for columnar caching. Larger batch sizes can improve memory utilization and compression, but risk OOMs when caching data.

 

其他配置選項

       下面的選項同樣可以用於查詢語句執行時的性能調優。在以後的發佈版本中可能會棄用這些選項,更多的將優化改爲自動執行。

計算機生成了可選文字:|0Property Name spark . sq 1 . fi 1 es . maxparti ti on8ytes spark . sq 1 . fi 1 es . openCostIn8ytes spark . sq 1 . aut08roadcastJoi n Threshol d spark. sq 1 . shuffle. parti ti ons Default 134217728 (128 MB) 4194304 (4 MB) 10485760 (10 MB) 200 Meaning The maximum number of bytes to pack into a single partition when reading files. The estimated cost to open a file, measured by the number of bytes could be scanned in the same time. This is used when putting multiple files into a partition. It is better to over estimated, then the partitions with small files will be faster than partitions with bigger files (which is scheduled first). Configures the maximum size in bytes for a table that will be broadcast to all worker nodes when performing a join. By setting this value to -1 broadcasting can be disabled. Note that currently statistics are only supported for Hive Metastore tables where the command ANALYZE TABLE COMPUTE STATISTICS noscan has been run. Configures the number of partitions to use when shuffling data for joins or aggregations.

 

 

分佈式SQL引擎

       Spark SQL同樣可以使用JDBC/ODBC或者命令行接口來作爲一個分佈式查詢引擎。在這種模式中,終端用戶或者應用可以通過執行SQL查詢語句直接與Spark SQL進行交互,不需要寫任何代碼。

 

運行Thrift JDBC/ODBC服務

        Thrift JDBC/ODBC服務實現了與Hive1.2.1的一致性。你可以使用任意來自Spark或者Hive1.2.1beeline script來測試JDBC服務。

Spark目錄中運行以下命令來啓動JDBC/ODBC服務:

       這個腳本接受所有bin/spark-submit的命令行選項,再加上可以執行Hive屬性的 --hiveconf選項。你可以運行./sbin/start-thriftserver.sh--help來顯示所有可用的選項的完整列表。默認情況下,此服務在localhost:10000進行監聽。你可以通過配置環境變量來改變此運行狀態,比如:

計算機生成了可選文字:|0export HIVE_SERVER2_THRIFT_PORT=<1istening-port> export ./sbin/start-thriftserver.sh \ -master <master-uri> \

       或者系統屬性:

計算機生成了可選文字:|0./sbin/start-thriftserver.sh \ --hiveconf hive.server2. \ --hiveconf hive.server2. \ -master <master-uri>

       現在你可以使用beeline來測試Thrift JDBC/ODBC服務:

計算機生成了可選文字:|0./bin/beeline

       beeline中使用以下命令來連接到JDBC/ODBC

        beeline將會詢問用戶名和密碼。在非安全模式,可以簡單地在你的機器上輸入用戶名和空白的密碼。在安全模式下,請遵照beeline documentation中給出的說明。

       Hive的配置是在conf/目錄下的 hive-site.xml,core-site.xml和hdfs-site.xml文件中進行配置的。

       你也可以使用來自Hivebeeline script

Thrift JDBC服務同樣支持在HTTP傳輸上發送 thrift RPC消息。在系統屬性或者在conf/目錄中的hive-site.xml文件中進行以下設置來將模式設爲HTTP模式:

計算機生成了可選文字:|0hive . server 2. transport. mode Set this to value: http hive.server2. thrift.http.port - HTTP port number fo listen on; default is 10001 hive.server2.http.endpoint - HTTP endpoint; default is cliservice

http模式中使用beeline連接到JDBC/ODBC來進行測試:

計算機生成了可選文字:|0beeline> !connect transport.mode=http;hive.serve r 2 . thri ft. http . path=<http_endpoi

 

運行Spark SQL命令行界面

        Spark SQL CLI是一個在本地模式下運行Hive元存儲服務和執行從命令行輸入的查詢語句的便捷的工具。注意Spark SQL CLIThriftJDBC服務不能通信。

你可以在Spark目錄中運行以下命令來開始Spark SQL CLI

        Hive的配置是在conf/目錄下的hive-site.xml, core-site.xml和hdfs-site.xml文件中進行配置的。你可以運行./sbin/start-thriftserver.sh--help來顯示所有可用的選項的完整列表。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章