Spark官網補缺

Spark官網補缺(2.3.4) RDD SparkSql

1.RDD

1.1文件格式

Spark的所有基於文件的輸入方法(包括textFile)都支持在目錄,壓縮文件和通配符上運行。例如,你可以使用textFile("/my/directory"),textFile("/my/directory/*.txt")和textFile("/my/directory/*.gz")。

1.2 shell操作

在Spark Shell中,已經在名爲的變量中爲您創建了一個特殊的可識別解釋器的SparkContext sc。製作自己的SparkContext將不起作用。您可以使用--master參數設置上下文連接到哪個主機,還可以通過將逗號分隔的列表傳遞給參數來將JAR添加到類路徑--jars。您還可以通過在--packages參數中提供逗號分隔的Maven座標列表,從而將依賴項(例如Spark Packages)添加到Shell會話中。可能存在依賴項的任何其他存儲庫(例如Sonatype)都可以傳遞給--repositories參數。例如,要bin/spark-shell在四個核心上運行,請使用:

$ ./bin/spark-shell --master local[4]
或者,也可以添加code.jar到其類路徑中,使用:

$ ./bin/spark-shell --master local[4] --jars code.jar
要使用Maven座標包含依賴項,請執行以下操作:

$ ./bin/spark-shell --master local[4] --packages "org.example:example:0.1"
有關選項的完整列表,請運行spark-shell --help。在後臺, spark-shell調用更通用的spark-submit腳本。

1.3 惰性加載

Spark中的所有轉換都是惰性的,因爲它們不會立即計算出結果。相反,他們只記得應用於某些基本數據集(例如文件)的轉換。僅當動作要求將結果返回給驅動程序時才計算轉換。這種設計使Spark可以更高效地運行。例如,我們可以通過map創建數據集使用,reduce並且僅將reduce的結果返回給驅動程序,而不是將較大的映射數據集返回給驅動程序。

1.4 緩存(持久化)

To illustrate RDD basics, consider the simple program below:

val lines = sc.textFile("data.txt")
val lineLengths = lines.map(s => s.length)
val totalLength = lineLengths.reduce((a, b) => a + b)
第一行定義了來自外部文件的基本RDD。這個數據集沒有加載到內存中,也沒有在其他地方執行:行只是指向文件的指針。第二行將lineLengths定義爲映射轉換的結果。同樣,由於懶惰,lineLengths不是立即計算的。最後,運行reduce,這是一個動作。此時,Spark將計算分解爲在不同的機器上運行的任務,每臺機器都運行其部分映射和局部約簡,只向驅動程序返回其答案。

If we also wanted to use lineLengths again later, we could add:

lineLengths.persist()
before the reduce, which would cause lineLengths to be saved in memory after the first time it is computed.

1.5shuffle有排序嗎? sorted blocks.

Internally, results from individual map tasks are kept in memory until they can’t fit. Then, these are sorted based on the target partition and written to a single file. On the reduce side, tasks read the relevant sorted blocks.
在內部,單個地圖任務的結果會保留在內存中,直到無法容納爲止。然後,根據目標分區對它們進行排序並寫入單個文件。在簡化方面,任務讀取相關的已排序塊。
所述shuffle是昂貴的操作,因爲它涉及的磁盤I/O,數據序列,和網絡I/O。爲了組織shuffle數據,Spark生成任務集-map任務以組織數據,以及一組reduce任務來聚合數據。此術語來自MapReduce(shuffle一詞),與Spark map和reduce操作沒有直接關係。
shuffle還會在磁盤上生成大量中間文件。從Spark 1.3開始,將保留這些文件,直到不再使用相應的RDD並進行垃圾回收爲止。這樣做是爲了在重新計算沿襲時無需重新創建隨機播放文件。如果應用程序保留了對這些RDD的引用,或者如果GC不經常啓動,則垃圾收集可能僅在很長一段時間後纔會發生。這意味着長時間運行的Spark作業可能會佔用大量磁盤空間。spark.local.dir在配置Spark上下文時,臨時存儲目錄由配置參數指定 。

可以通過調整各種配置參數來調整shuffle行爲。請參閱《Spark配置指南》中的“Shuffle Behavior”部分

1.6調優策略之序列化

數據序列化
序列化在任何分佈式應用程序的性能中都起着重要作用。將對象序列化爲慢速格式或佔用大量字節的格式將大大減慢計算速度。通常,這是您應該優化Spark應用程序的第一件事。Spark旨在在便利性(允許您在操作中使用任何Java類型)和性能之間取得平衡。它提供了兩個序列化庫:

Java序列化:默認情況下,Spark使用Java的ObjectOutputStream框架對對象進行序列化,並且可以與您創建的實現的任何類一起使用 java.io.Serializable。您還可以通過擴展來更緊密地控制序列化的性能 java.io.Externalizable。Java序列化很靈活,但是通常很慢,並且導致許多類的序列化格式很大。
Kryo序列化:Spark還可以使用Kryo庫(版本2)更快地序列化對象。與Java序列化(通常多達10倍)相比,Kryo顯着更快,更緊湊,但是Kryo不支持所有 Serializable類型,並且要求您預先註冊要在程序中使用的類,以實現最佳性能。
您可以通過使用SparkConf初始化作業 並調用來切換爲使用Kryo conf.set("spark.serializer","org.apache.spark.serializer.KryoSerializer")。

要使用Kryo註冊您自己的自定義類,請使用registerKryoClasses方法。

val conf = new SparkConf().setMaster(...).setAppName(...)
conf.registerKryoClasses(Array(classOf[MyClass1], classOf[MyClass2]))
val sc = new SparkContext(conf)

1.7調優策略之內存調優

Spark中的內存使用情況大體上屬於以下兩類之一:執行和存儲。執行內存是指用於 shuffles, joins, sorts and aggregations, 的計算的內存,而存儲內存是指用於在集羣中緩存和傳播內部數據的內存。
。在Spark中,執行和存儲共享一個統一的區域(M)。當不使用執行內存時,存儲可以獲取所有可用內存,反之亦然。如果有必要,執行可能會驅逐存儲,但只有在總存儲內存使用率下降到某個閾值(R)以下時,纔可以執行該操作。換句話說,R描述了一個子區域,在該子區域M中永遠不會清除緩存的塊。由於實現的複雜性,存儲可能無法退出執行。(暫時理解存儲是可以被執行的內存請求到,從而沒有存儲,而執行的內存一定在的)
這種設計確保了幾種理想的性能。首先,不使用緩存的應用程序可以將整個空間用於執行,從而避免了不必要的磁盤溢出。其次,確實使用緩存的應用程序可以保留最小的存儲空間(R),以免其數據塊被逐出。最後,這種方法可爲各種工作負載提供合理的即用即用性能,而無需用戶瞭解如何在內部劃分內存。

儘管有兩種相關的配置,但典型用戶無需調整它們,因爲默認值適用於大多數工作負載:

spark.memory.fraction表示的大小M爲(JVM堆空間-300MB)的一部分(默認值爲0.6)。其餘空間(40%)保留用於用戶數據結構,Spark中的內部元數據,並在記錄稀疏和異常大的情況下防止OOM錯誤。
spark.memory.storageFraction將的大小表示R爲的一部分M(默認爲0.5)。 R是M其中的緩存塊不受執行影響而退出的存儲空間。
spark.memory.fraction應該設置的值,以便在JVM的舊版本或“長期使用的”一代中舒適地適應此堆空間量。有關詳細信息,請參見下面有關高級GC調整的討論。

1.7.1數據結構

調整數據結構
減少內存消耗的第一種方法是避免增加開銷的Java特性,比如基於指針的數據結構和包裝器對象。有幾種方法可以做到這一點:
設計您的數據結構以選擇對象數組和基本類型,而不是標準的Java或Scala集合類(例如HashMap)。fastutil庫爲與Java標準庫兼容的基本類型提供了方便的集合類。
儘可能避免嵌套有大量小對象和指針的結構。
考慮使用數字id或枚舉對象代替鍵的字符串。
如果您的內存不足32 GB,可以設置JVM標誌-XX:+UseCompressedOops,使指針爲4字節,而不是8字節。您可以在spark-env.sh中添加這些選項。

1.7.2序列化

當您的對象仍然太大而無法進行優化存儲時,減少內存使用的一種更簡單的方法是使用RDD持久性API中的序列化StorageLevels (例如)以序列化形式存儲它們。然後,Spark將每個RDD分區存儲爲一個大字節數組。由於必須動態地反序列化每個對象,因此以串行形式存儲數據的唯一缺點是訪問時間較慢。如果您想以序列化形式緩存數據,我們強烈建議使用Kryo,因爲它導致的大小比Java序列化(當然也比原始Java對象)小。MEMORY_ONLY_SER

1.7.3gc優化

當您的程序存儲了大量的RDDs時,JVM垃圾收集可能會成爲一個問題。(對於那些只讀取一次RDD,然後在上面運行許多操作的程序來說,這通常不是問題。)當Java需要清除舊對象以爲新對象騰出空間時,它將需要跟蹤所有Java對象並找到未使用的對象。這裏要記住的要點是,垃圾收集的成本與Java對象的數量成正比,因此使用對象較少的數據結構(例如,一個Ints數組而不是一個LinkedList)可以大大降低此成本。
衡量GC的影響

GC調整的第一步是收集有關垃圾收集發生頻率和花費GC時間的統計信息。這可以通過添加-verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStampsJava選項來完成。(有關將Java選項傳遞給Spark作業的信息,請參閱配置指南。)下次運行Spark作業時,每次發生垃圾收集時,您都會在工作日誌中看到打印的消息。請注意,這些日誌將位於羣集的工作節點上(stdout位於其工作目錄中的文件中),而不是驅動程序上。

高級GC調整

爲了進一步調整垃圾回收,我們首先需要了解有關JVM中內存管理的一些基本信息:

Java Heap空間分爲Young和Old兩個區域。年輕一代用於保存壽命短的對象,而老一代則用於壽命更長的對象。

年輕一代又分爲三個區域[Eden,Survivor1,Survivor2]。

垃圾收集過程的簡化描述:當Eden已滿時,將在Eden上運行次要GC,並將來自Eden和Survivor1的活動對象複製到Survivor2。倖存者區域被交換。如果對象足夠舊或Survivor2已滿,則將其移到“舊”。最後,當Old接近滿時,將調用完整的GC。

在Spark中進行GC調整的目標是確保在舊一代中僅存儲長壽命的RDD,並確保新世代具有足夠的大小以存儲短壽命的對象。這將有助於避免完整的GC收集任務執行期間創建的臨時對象。可能有用的一些步驟是:
通過收集GC統計信息來檢查是否有太多的垃圾收集。如果在任務完成之前多次調用full GC,這意味着沒有足夠的可用內存來執行任務。
如果minor collections but not many major GCs,那麼爲Eden分配更多的內存會有所幫助。您可以將Eden的大小設置爲高估每個任務所需的內存大小。如果伊甸園的大小被確定爲E,那麼您可以使用選項-Xmn=4/3*E設置年輕代的大小。(4/3的比例也考慮到了倖存者區域使用的空間。)
在打印的GC統計數據中,如果oldGEN接近滿,通過降低spark.memory.fraction來減少用於緩存的內存量;緩存更少的對象比降低任務執行速度要好。或者,考慮減少年輕一代的規模。這意味着降低-Xmn,如果你把它設爲上面的值。如果沒有,嘗試更改JVM的NewRatio參數的值。許多jvm將其默認爲2,這意味着老一代佔用了2/3的堆。它應該足夠大以至於這個分數超過火花。記憶。分數。
嘗試使用-XX:+UseG1GC的G1GC垃圾收集器。在垃圾收集成爲瓶頸的某些情況下,它可以提高性能。注意,對於較大的執行程序堆大小,使用-XX:G1HeapRegionSize來增加G1區域大小可能很重要
如果您的任務是從HDFS讀取數據,則可以使用從HDFS讀取的數據塊的大小來估算任務使用的內存量。注意,解壓縮塊的大小通常是塊大小的2或3倍。因此,如果我們希望擁有3或4個任務的工作空間,並且HDFS塊大小爲128 MB,則我們可以估計Eden的大小爲4 * 3 * 128MB。

監視垃圾收集所花費的頻率和時間隨新設置的變化。

1.8調優的其他配置

並行度
除非將每個操作的並行度設置得足夠高,否則集羣不會得到充分利用。Spark根據每個文件的大小自動設置要在每個文件上運行的map任務的數量(儘管您可以通過SparkContext的可選參數來控制它。對於分佈式reduce操作,例如groupByKey和reduceByKey,它使用最大的父RDD的分區數。您可以將並行度級別作爲第二個參數傳遞(參見spark),或者設置config屬性spark.default.parallelism來更改默認值。通常,我們建議在您的集羣中每個CPU內核執行2-3個任務。


減少reduce任務的內存使用
有時,您會收到OutOfMemoryError的原因不是因爲您的RDD不能容納在內存中,而是因爲您的一項任務(例如中的reduce任務之一)的工作集groupByKey太大。spark的reduce操作(sortByKey,groupByKey,reduceByKey,join,等)建立每個任務中的哈希表來進行分組,而這往往是大的。此處最簡單的解決方法是 提高並行度,以使每個任務的輸入集更小。Spark可以高效地支持短至200 ms的任務,因爲它可以在多個任務中重用一個執行器JVM,並且任務啓動成本低,因此您可以安全地將並行級別提高到集羣中核心的數量以上。

廣播大變量
使用SparkContext中可用的廣播功能可以極大地減少每個序列化任務的大小,以及通過集羣啓動作業的成本。如果您的任務使用了驅動程序中的任何大對象(例如,一個靜態查找表),請考慮將其轉換爲廣播變量。Spark在主服務器上打印每個任務的序列化大小,因此您可以查看它來確定任務是否太大;一般來說,大於20kb的任務可能值得優化。

數據局部性
數據局部性可能會對Spark作業的性能產生重大影響。如果數據和對其進行操作的代碼在一起,則計算速度往往會很快。但是,如果代碼和數據是分開的,那麼一個必須移到另一個。通常,從一個地方到另一個地方傳送序列化代碼要比塊數據更快,因爲代碼大小比數據小得多。Spark圍繞此數據本地性一般原則構建調度。

數據局部性是數據與處理它的代碼之間的接近程度。根據數據的當前位置,可分爲多個級別。從最遠到最遠的順序:

PROCESS_LOCAL數據與正在運行的代碼位於同一JVM中。這是最好的位置
NODE_LOCAL數據在同一節點上。示例可能在同一節點上的HDFS中,或者在同一節點上的另一執行程序中。這比PROCESS_LOCAL由於數據必須在進程之間傳輸而要慢一些
NO_PREF 可以從任何地方快速訪問數據,並且不受位置限制
RACK_LOCAL數據位於同一服務器機架上。數據位於同一機架上的其他服務器上,因此通常需要通過單個交換機通過網絡發送
ANY 數據在網絡上的其他位置,而不是在同一機架中
Spark傾向於在最佳位置級別安排所有任務,但這並不總是可能的。在任何空閒執行器上沒有未處理數據的情況下,Spark會切換到較低的本地級別。有兩種選擇:a)等待忙碌的CPU釋放以在同一服務器上的數據上啓動任務,或b)立即在需要將數據移動到更遠的地方啓動新任務。

Spark通常要做的是稍等一下,以期釋放繁忙的CPU。一旦超時到期,它將開始將數據從很遠的地方移到空閒的CPU中。每個級別之間的回退等待超時可以單獨配置,也可以一起配置在一個參數中。有關詳細信息,請參見配置頁面spark.locality上的 參數。如果您的任務很長並且位置不佳,則應該增加這些設置,但是默認設置通常效果很好。

2.Spark Sql

2.1.1全局臨時視圖

Spark SQL中的臨時視圖是會話作用域的,如果創建它的會話終止,它將消失。如果要在所有會話之間共享一個臨時視圖並保持活動狀態,直到Spark應用程序終止,則可以創建全局臨時視圖。全局臨時視圖與系統保留的數據庫相關聯global_temp,我們必須使用限定名稱來引用它,例如SELECT * FROM global_temp.view1

2.2.1spark dataframe類型簡介

Spark SQL是一個用於結構化數據處理的Spark模塊。與基本的Spark RDD API不同,Spark SQL提供的接口爲Spark提供了有關數據結構和正在執行的計算的更多信息。在內部,Spark SQL使用這些額外的信息來執行額外的優化。有幾種與Spark SQL交互的方法,包括SQL和Dataset API。當計算結果時,使用相同的執行引擎,而不依賴於使用哪種API/語言來表示計算。這種統一意味着開發人員可以很容易地在不同的api之間來回切換,而這些api提供了表達給定轉換的最自然的方式。本頁上的所有示例都使用Spark發行版中包含的示例數據,可以在Spark -shell、pyspark shell或sparkR shell中運行。
schema:
Spark SQL提供的接口爲Spark提供了有關數據結構和正在執行的計算的更多信息。在內部,Spark SQL使用這些額外的信息來執行額外的優化。

2.2.2Dataframe和DataSet的row類型

DataFrames provide a domain-specific language for structured data manipulation in Scala, Java, Python and R. As mentioned above, in Spark 2.0, DataFrames are just Dataset of Rows in Scala and Java API. These operations are also referred as “untyped transformations” in contrast to “typed transformations” come with strongly typed Scala/Java Datasets. Here we include some basic examples of structured data processing using Datasets:
中文(簡體)
DataFrame爲Scala,Java,Python和R中的結構化數據操作提供了一種特定於域的語言。 如上所述,在Spark 2.0中,DataFrame只是Scala和Java API中的row的DATASET。與強類型的Scala / Java數據集附帶的“類型轉換”相反,這些操作也稱爲“非類型轉換”。 這裏我們包括一些使用數據集進行結構化數據處理的基本示例:

2.2.3數據集的序列化

Datasets are similar to RDDs, however, instead of using Java serialization or Kryo they use a specialized Encoder to serialize the objects for processing or transmitting over the network. While both encoders and standard serialization are responsible for turning an object into bytes, encoders are code generated dynamically and use a format that allows Spark to perform many operations like filtering, sorting and hashing without deserializing the bytes back into an object.

數據集類似於RDD,但是,它們不使用Java序列化或Kryo,而是使用專用的Encoder對對象進行序列化以進行處理或通過網絡傳輸。雖然編碼器和標準序列化都負責將對象轉換爲字節,但是編碼器是動態生成的代碼,並使用一種格式,該格式允許Spark執行許多操作,如過濾,排序和哈希處理,而無需將字節反序列化爲對象。

2.2.4與RDD的互操作

Spark SQL支持兩種將現有RDD轉換爲數據集的方法。第一種方法使用反射來推斷包含特定對象類型的RDD的架構。這種基於反射的方法可以使代碼更簡潔,當您在編寫Spark應用程序時已經瞭解架構時,可以很好地工作。

創建數據集的第二種方法是通過編程界面,該界面允許您構造模式,然後將其應用於現有的RDD。儘管此方法較爲冗長,但可以在運行時才知道列及其類型的情況下構造數據集。
// For implicit conversions from RDDs to DataFrames
import spark.implicits._

// Create an RDD of Person objects from a text file, convert it to a Dataframe
val peopleDF = spark.sparkContext
  .textFile("examples/src/main/resources/people.txt")
  .map(_.split(","))
  .map(attributes => Person(attributes(0), attributes(1).trim.toInt))
  .toDF()
// Register the DataFrame as a temporary view
peopleDF.createOrReplaceTempView("people")

// SQL statements can be run by using the sql 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: " + teenager(0)).show()
// +------------+
// |       value|
// +------------+
// |Name: Justin|
// +------------+

// or by field name
teenagersDF.map(teenager => "Name: " + teenager.getAs[String]("name")).show()
// +------------+
// |       value|
// +------------+
// |Name: Justin|
// +------------+

// No pre-defined encoders for Dataset[Map[K,V]], define explicitly
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]
// Primitive types and case classes can be also defined as
// implicit val stringIntMapEncoder: Encoder[Map[String, Any]] = ExpressionEncoder()

// row.getValuesMap[T] retrieves multiple columns at once into a Map[String, T]
teenagersDF.map(teenager => teenager.getValuesMap[Any](List("name", "age"))).collect()
// Array(Map("name" -> "Justin", "age" -> 19))

import org.apache.spark.sql.types._

// Create an RDD
val peopleRDD = spark.sparkContext.textFile("examples/src/main/resources/people.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(fieldName => StructField(fieldName, StringType, nullable = true))
val schema = StructType(fields)

// Convert records of the RDD (people) to Rows
val rowRDD = peopleRDD
  .map(_.split(","))
  .map(attributes => Row(attributes(0), attributes(1).trim))

// Apply the schema to the RDD
val peopleDF = spark.createDataFrame(rowRDD, schema)

// Creates a temporary view using the DataFrame
peopleDF.createOrReplaceTempView("people")

// 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(0)).show()
// +-------------+
// |        value|
// +-------------+
// |Name: Michael|
// |   Name: Andy|
// | Name: Justin|
// +-------------+

2.2.5spark的隱式轉換

1、toDF的隱式轉換

import spark.implicits._

2、spark.read.kudu中的kudu的隱式轉換

improt org.apache.kudu.spark.kudu._
#spark指的是代碼中創建的對象

3.asjava的隱式轉換
import scala.collection.JavaConverters._

4.通過schema構建df時,設置StructField的Type
import org.apache.spark.sql.types._

5.定義map[String,Any]的反序列化格式kryo
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]

2.2.6用戶自定義的聚合函數(UDAF)

這裏不做介紹

2.2.7數據源

數據源
Spark SQL支持通過DataFrame接口對各種數據源進行操作。DataFrame可以使用關係轉換進行操作,也可以用於創建臨時視圖。將DataFrame註冊爲臨時視圖使您可以對其數據運行SQL查詢。本節介紹了使用Spark數據源加載和保存數據的一般方法,然後介紹了可用於內置數據源的特定選項
以最簡單的形式,所有操作都將使用默認數據源(parquet除非另有配置 spark.sql.sources.default)。
實例:默認parquet的格式
val usersDF = spark.read.load("examples/src/main/resources/users.parquet")
usersDF.select("name", "favorite_color").write.save("namesAndFavColors.parquet")
手動指定選項
您還可以手動指定將要使用的數據源以及要傳遞給數據源的任何其他選項。數據源通過其全名指定(即org.apache.spark.sql.parquet),但內置的來源,你也可以使用自己的短名稱(json,parquet,jdbc,orc,libsvm,csv,text)。從任何數據源類型加載的DataFrame都可以使用此語法轉換爲其他類型。
val peopleDF = spark.read.format("json").load("examples/src/main/resources/people.json")
peopleDF.select("name", "age").write.format("parquet").save("namesAndAges.parquet")
要加載JSON文件,您可以使用:
option
val peopleDFCsv = spark.read.format("csv")
  .option("sep", ";")
  .option("inferSchema", "true")
  .option("header", "true")
  .load("examples/src/main/resources/people.csv")

2.2.8savemode

保存操作可以選擇帶SaveMode,指定如何處理現有數據(如果存在)。重要的是要認識到這些保存模式不利用任何鎖定,也不是原子的。另外,執行時Overwrite,將在寫出新數據之前刪除數據。

Scala / Java 任何語言 含義
SaveMode.ErrorIfExists (默認) "error" or "errorifexists" (默認) 將DataFrame保存到數據源時,如果已經存在數據,則將引發異常。
SaveMode.Append "append" 將DataFrame保存到數據源時,如果已經存在數據/表,則應該將DataFrame的內容附加到現有數據中。
SaveMode.Overwrite "overwrite" 覆蓋模式意味着將DataFrame保存到數據源時,如果已經存在數據/表,則預期現有數據將被DataFrame的內容覆蓋。
SaveMode.Ignore "ignore" 忽略模式意味着在將DataFrame保存到數據源時,如果已經存在數據,則預期保存操作不會保存DataFrame的內容並且不會更改現有數據。這類似於CREATE TABLE IF NOT EXISTSSQL中的。

2.2.9saveAsTable

`DataFrames`也可以使用以下`saveAsTable` 命令作爲持久表保存到Hive Metastore中。請注意,使用此功能不需要現有的Hive部署。Spark將爲您創建一個默認的本地Hive Metastore(使用Derby)。與`createOrReplaceTempView`命令不同, `saveAsTable`它將具體化DataFrame的內容並在Hive元存儲中創建一個指向數據的指針。即使您重新啓動Spark程序,持久表仍將存在,只要您保持與同一metastore的連接即可。可以通過使用表名稱`table`在上調用方法來創建持久表的DataFrame `SparkSession`。
對於基於文件的數據源,例如文本,鑲木地板,json等),您可以通過path選項指定自定義表格路徑,
例如df.write.option(“ path”,“ /some/path").saveAsTable("t”)。
刪除表後,自定義表路徑將不會刪除,並且表數據仍然存在。如果未指定自定義表路徑,Spark會將數據寫入倉庫目錄下的默認表路徑。刪除表後,默認表路徑也將被刪除

2.2.9.1save時指定分區\分桶\排序

peopleDF.write.bucketBy(42, "name").sortBy("age").saveAsTable("people_bucketed")
usersDF.write.partitionBy("favorite_color").format("parquet").save("namesPartByColor.parquet")
可以對單個表使用分區和存儲桶:
usersDF
  .write
  .partitionBy("favorite_color")
  .bucketBy(42, "name")
  .saveAsTable("users_partitioned_bucketed")
  partitionBy創建一個目錄結構,如分區發現部分所述。因此,它對具有高基數的列的適用性有限。相反,bucketBy將數據分佈在固定數量的桶上,並且可以在許多惟一值是無界的情況下使用。

2.2.9.2read / write parquet

parquet是一種柱狀格式,得到許多其他數據處理系統的支持。Spark SQL支持讀寫拼花文件,自動保存原始數據的模式。在編寫parquet文件時,出於兼容性的考慮,所有列都會自動轉換爲可空。

2.3.0表分區

表分區是Hive等系統中常用的優化方法。在分區表中,數據通常存儲在不同的目錄中,分區列值編碼在每個分區目錄的路徑中。所有內置的文件源(包括文本/CSV/JSON/ORC/Parquet)都能夠自動發現和推斷分區信息。例如,我們可以使用以下目錄結構將以前使用的所有人口數據存儲到分區表中,並添加兩個額外的列,性別和國家作爲分區列:

通過傳遞path/to/table給SparkSession.read.parquet或SparkSession.read.load,Spark SQL將自動從路徑中提取分區信息。現在,返回的DataFrame的架構變爲:
與ProtocolBuffer、Avro和Thrift一樣,Parquet也支持模式演化。用戶可以從一個簡單的模式開始,然後根據需要逐步向模式中添加更多的列。這樣,用戶可能會得到多個模式不同但相互兼容的parquet文件。Parquet數據源現在能夠自動檢測這種情況併合並所有這些文件的模式。
由於模式合併是一個相對昂貴的操作,並且在大多數情況下不是必需的,所以我們從1.5.0開始默認關閉它。你可以使它成爲可能
讀取parquet文件時將數據源選項mergeSchema設置爲true(如下面的示例所示),或者
設置全局SQL選項spark.sql.parquet.mergeSchema爲true。
// 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, i * i)).toDF("value", "square")
squaresDF.write.parquet("data/test_table/key=1")

// 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 => (i, i * i * i)).toDF("value", "cube")
cubesDF.write.parquet("data/test_table/key=2")

// Read the partitioned table
val mergedDF = spark.read.option("mergeSchema", "true").parquet("data/test_table")
mergedDF.printSchema()

// 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)

|value|square|cube|key|
+-----+------+----+---+
|    3|     9|null|  1|
|    4|    16|null|  1|
|    5|    25|null|  1|
|    8|  null| 512|  2|
|    9|  null| 729|  2|
|   10|  null|1000|  2|
|    1|     1|null|  1|
|    2|     4|null|  1|
|    6|  null| 216|  2|
|    7|  null| 343|  2|
+-----+------+----+---+

2.3.1 hive metastore Parquet table 轉換

Hive metastore Parquet table conversion
當讀取和寫入Hive metastore Parquet時,Spark SQL將嘗試使用自己的 Parquet支持,而不是Hive SerDe,以獲得更好的性能。此行爲由spark.sql.hive控制。轉換spark.sql.hive.convertMetastoreParquet配置,默認爲true。
從表模式處理的角度來看,Hive和Parquet之間有兩個關鍵區別。
Hive不區分大小寫,而Parquet不區分大小寫
Hive認爲所有列都可爲空,而Parquet中的可爲空性很重要
因此,在將一個Hive metastore Parquet table轉換成一個Spark SQL Parquet table時,我們必須協調Hive metastore schema和Parquet schema。
和解規則如下:
無論是否爲空,兩個模式中具有相同名稱的字段必須具有相同的數據類型。調整後的字段應該具有Parquet side的數據類型,以保證可爲空。
協調模式包含在Hive metastore模式中定義的字段。
只出現在parquet模式中的任何字段都將在協調模式中刪除。
任何只出現在Hive metastore模式中的字段都會被添加到和解模式中作爲可空字段。

2.3.2元數據刷新

Spark SQL緩存Parquet元數據以獲得更好的性能。啓用Hive Metastore Parquet錶轉換後,這些轉換表的元數據也會被緩存。如果這些表是通過Hive或其他外部工具更新的,則需要手動刷新它們以確保元數據一致。

// spark is an existing SparkSession
spark.catalog.refreshTable("my_table")

2.3.3 Configuration

可以使用setConfon中的方法SparkSessionSET key=value使用SQL 運行 命令來完成Parquet的配置。

物業名稱 默認 含義
spark.sql.parquet.binaryAsString false 編寫Parquet模式時,其他一些Parquet產生系統,尤其是Impala,Hive和舊版本的Spark SQL,不會區分二進制數據和字符串。該標誌告訴Spark SQL將二進制數據解釋爲字符串,以提供與這些系統的兼容性。
spark.sql.parquet.int96AsTimestamp true 一些Parquet-producing系統,特別是Impala和Hive,將時間戳存儲在INT96中。此標誌告訴Spark SQL將INT96數據解釋爲時間戳,以提供與這些系統的兼容性。
spark.sql.parquet.compression.codec snappy 設置編寫Parquet文件時使用的壓縮編解碼器。如果在特定於表的選項/屬性中指定了“壓縮”或“ parquet.compression”,則優先級爲“壓縮”,“ parquet.compression”,“ spark.sql.parquet.compression.codec”。可接受的值包括:none,uncompressed,snappy,gzip,lzo。
spark.sql.parquet.filterPushdown true 設置爲true時啓用Parquet過濾器下push-down 優化。
spark.sql.hive.convertMetastoreParquet true 設置爲false時,Spark SQL將使用Hive SerDe用於Parquet表,而不是內置支持。
spark.sql.parquet.mergeSchema false 設置爲true時,Parquet數據源合併從所有數據文件收集的架構,否則從摘要文件或隨機數據文件(如果沒有摘要文件可用)中選擇架構。

2.3.4 ORC文件

從Spark 2.3開始,Spark就支持一個帶新ORC文件格式的矢量化ORC讀取器。爲此,新添加了以下配置。當spark.sql.orc時,向量化的讀取器用於本地ORC表(例如,使用ORC子句創建的表)。impl被設置爲native和spark.sql.orc。enableVectorizedReader設置爲true。對於Hive ORC serde表(例如,使用Hive選項子句USING HIVE OPTIONS (fileFormat 'ORC'),向量化的讀取器在spark.sql.hive時使用。convertMetastoreOrc也被設置爲true。

物業名稱 默認 含義
spark.sql.orc.impl hive ORC實現的名稱。可以是native和之一hivenative表示基於Apache ORC 1.4.1構建的本機ORC支持。“ hive”是指Hive 1.2.1中的ORC庫。
spark.sql.orc.enableVectorizedReader true native實現中啓用向量化orc解碼。如果爲false,則在native實現中使用新的非矢量化ORC讀取器。爲了hive實現,這被忽略。

2.3.5 JSON數據集

Spark SQL可以自動推斷JSON數據集的架構並將其作爲加載Dataset[Row]。這種轉換是可以做到用SparkSession.read.json()在任一Dataset[String]或JSON文件。

請注意,以json文件形式提供的文件不是典型的JSON文件。每行必須包含一個單獨的,自包含的有效JSON對象。有關更多信息,請參見 JSON Lines文本格式,也稱爲newline分隔的JSON

對於常規的多行JSON文件,請將multiLine選項設置爲true

// Primitive types (Int, String, etc) and Product types (case classes) encoders are
// supported by importing this when creating a Dataset.
import spark.implicits._

// 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 = "examples/src/main/resources/people.json"
val peopleDF = spark.read.json(path)

// The inferred schema can be visualized using the printSchema() method
peopleDF.printSchema()
// root
//  |-- age: long (nullable = true)
//  |-- name: string (nullable = true)

// Creates a temporary view using the DataFrame
peopleDF.createOrReplaceTempView("people")

// SQL statements can be run by using the sql methods provided by spark
val teenagerNamesDF = spark.sql("SELECT 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
// a Dataset[String] storing one JSON object per string
val otherPeopleDataset = spark.createDataset(
  """{"name":"Yin","address":{"city":"Columbus","state":"Ohio"}}""" :: Nil)
val otherPeople = spark.read.json(otherPeopleDataset)
otherPeople.show()
// +---------------+----+
// |        address|name|
// +---------------+----+
// |[Columbus,Ohio]| Yin|
// +---------------+----+

2.3.6 hive table

Spark SQL還支持讀寫存儲在Apache Hive中的數據。但是,由於Hive具有大量依賴關係,因此默認的Spark分發中不包含這些依賴關係。如果可以在類路徑上找到Hive依賴項,Spark將自動加載它們。請注意,這些Hive依賴項也必須存在於所有工作節點上,因爲它們將需要訪問Hive序列化和反序列化庫(SerDes)才能訪問存儲在Hive中的數據。

通過將hive-site.xml,core-site.xml(對於安全性配置)和hdfs-site.xml(對於HDFS配置)文件放置在中來配置Hive conf/。

使用Hive時,必須實例化SparkSessionHive支持,包括與持久性Hive元存儲庫的連接,對Hive Serdes的支持以及Hive用戶定義的功能。沒有現有Hive部署的用戶仍可以啓用Hive支持。如果未由配置hive-site.xml,則上下文會自動metastore_db在當前目錄中創建並創建由配置spark.sql.warehouse.dir的目錄spark-warehouse,該目錄默認 爲啓動Spark應用程序的當前目錄中的目錄。請注意,自Spark 2.0.0起不推薦使用hive.metastore.warehouse.dirin 的屬性hive-site.xml。而是使用spark.sql.warehouse.dir指定倉庫中數據庫的默認位置。您可能需要向啓動Spark應用程序的用戶授予寫權限。
import java.io.File

import org.apache.spark.sql.{Row, SaveMode, SparkSession}

case class Record(key: Int, value: String)

// warehouseLocation points to the default location for managed databases and tables
val warehouseLocation = new File("spark-warehouse").getAbsolutePath

val spark = SparkSession
  .builder()
  .appName("Spark Hive Example")
  .config("spark.sql.warehouse.dir", warehouseLocation)
  .enableHiveSupport()
  .getOrCreate()

import spark.implicits._
import spark.sql

sql("CREATE TABLE IF NOT EXISTS src (key INT, value STRING) USING hive")
sql("LOAD DATA LOCAL INPATH 'examples/src/main/resources/kv1.txt' INTO TABLE src")

// Queries are expressed in HiveQL
sql("SELECT * FROM src").show()
// +---+-------+
// |key|  value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Aggregation queries are also supported.
sql("SELECT COUNT(*) FROM src").show()
// +--------+
// |count(1)|
// +--------+
// |    500 |
// +--------+

// The results of SQL queries are themselves DataFrames and support all normal functions.
val sqlDF = sql("SELECT key, value FROM src WHERE key < 10 ORDER BY key")

// The items in DataFrames are of type Row, which allows you to access each column by ordinal.
val stringsDS = sqlDF.map {
  case Row(key: Int, value: String) => s"Key: $key, Value: $value"
}
stringsDS.show()
// +--------------------+
// |               value|
// +--------------------+
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// |Key: 0, Value: val_0|
// ...

// You can also use DataFrames to create temporary views within a SparkSession.
val recordsDF = spark.createDataFrame((1 to 100).map(i => Record(i, s"val_$i")))
recordsDF.createOrReplaceTempView("records")

// Queries can then join DataFrame data with data stored in Hive.
sql("SELECT * FROM records r JOIN src s ON r.key = s.key").show()
// +---+------+---+------+
// |key| value|key| value|
// +---+------+---+------+
// |  2| val_2|  2| val_2|
// |  4| val_4|  4| val_4|
// |  5| val_5|  5| val_5|
// ...

// Create a Hive managed Parquet table, with HQL syntax instead of the Spark SQL native syntax
// `USING hive`
sql("CREATE TABLE hive_records(key int, value string) STORED AS PARQUET")
// Save DataFrame to the Hive managed table
val df = spark.table("src")
df.write.mode(SaveMode.Overwrite).saveAsTable("hive_records")
// After insertion, the Hive managed table has data now
sql("SELECT * FROM hive_records").show()
// +---+-------+
// |key|  value|
// +---+-------+
// |238|val_238|
// | 86| val_86|
// |311|val_311|
// ...

// Prepare a Parquet data directory
val dataDir = "/tmp/parquet_data"
spark.range(10).write.parquet(dataDir)
// Create a Hive external Parquet table
sql(s"CREATE EXTERNAL TABLE hive_bigints(id bigint) STORED AS PARQUET LOCATION '$dataDir'")
// The Hive external table should already have data
sql("SELECT * FROM hive_bigints").show()
// +---+
// | id|
// +---+
// |  0|
// |  1|
// |  2|
// ... Order may vary, as spark processes the partitions in parallel.

// Turn on flag for Hive Dynamic Partitioning
spark.sqlContext.setConf("hive.exec.dynamic.partition", "true")
spark.sqlContext.setConf("hive.exec.dynamic.partition.mode", "nonstrict")
// Create a Hive partitioned table using DataFrame API
df.write.partitionBy("key").format("hive").saveAsTable("hive_part_tbl")
// Partitioned column `key` will be moved to the end of the schema.
sql("SELECT * FROM hive_part_tbl").show()
// +-------+---+
// |  value|key|
// +-------+---+
// |val_238|238|
// | val_86| 86|
// |val_311|311|
// ...

spark.stop()

2.3.7 hive的存儲格式

請注意,創建表時尚不支持Hive存儲處理程序,您可以在Hive端使用存儲處理程序創建表,並使用Spark SQL讀取表。

創建Hive表時,需要定義此表應如何從文件系統讀取/寫入數據,即“輸入格式”和“輸出格式”。您還需要定義該表應如何將數據反序列化爲行,或將行序列化爲數據,即“ serde”。以下選項可用於指定存儲格式(“ serde”,“ input format”,“ output format”),例如CREATE TABLE src(id int) USING hive OPTIONS(fileFormat 'parquet')。默認情況下,我們將以純文本形式讀取表文件。請注意,創建表時尚不支持Hive存儲處理程序,您可以在Hive端使用存儲處理程序創建表,並使用Spark SQL讀取表。
field 含義
fileFormat fileFormat是一種存儲格式規範的軟件包,其中包括“ serde”,“ input format”和“ output format”。目前,我們支持6種文件格式:“ sequencefile”,“ rcfile”,“ orc”,“ parquet”,“ textfile”和“ avro”。
inputFormat, outputFormat 這2個選項將相應的InputFormat和OutputFormat類的名稱指定爲字符串文字,例如org.apache.hadoop.hive.ql.io.orc.OrcInputFormat。這兩個選項必須成對出現,如果已經指定了fileFormat選項,則不能指定它們。
serde 此選項指定Serde類的名稱。當指定fileFormat選項時,如果給定的fileFormat已經包含serde信息,則不要指定此選項。當前,“ sequencefile”,“ textfile”和“ rcfile”不包括SERDE信息,您可以將此選項與這三種文件格式一起使用。
fieldDelim, escapeDelim, collectionDelim, mapkeyDelim, lineDelim 這些選項只能與“文本文件” fileFormat一起使用。它們定義瞭如何將定界文件讀取爲行。

定義的所有其他屬性OPTIONS將被視爲Hive serde屬性。

2.3.8 與Hive Metastore的不同版本進行交互

請注意,與用於與metastore進行通信的Hive版本無關,Spark SQL在內部將針對Hive 1.2.1進行編譯

與Hive metastore的交互是Spark SQL對Hive的最重要支持之一,它使Spark SQL能夠訪問Hive表的元數據。從Spark 1.4.0開始,使用以下描述的配置,可以使用Spark SQL的單個二進制版本來查詢Hive元存儲庫的不同版本。請注意,與用於與metastore進行通信的Hive版本無關,Spark SQL在內部將針對Hive 1.2.1進行編譯,並將這些類用於內部執行(serdes,UDF,UDAF等)。

可以使用以下選項來配置用於檢索元數據的Hive版本:
field 默認 含義
spark.sql.hive.metastore.version 1.2.1 Hive Metastore的版本。可用的選項是0.12.0通過1.2.1
spark.sql.hive.metastore.jars builtin 用於實例化HiveMetastoreClient的jar的位置。此屬性可以是三個選項之一:builtin使用Hive 1.2.1,該模塊在-Phive啓用時與Spark組件捆綁在一起。選擇此選項時,spark.sql.hive.metastore.version必須1.2.1定義或不定義。maven使用從Maven存儲庫下載的指定版本的Hive jar。通常不建議將此配置用於生產部署。JVM標準格式的類路徑。這個類路徑必須包含所有的Hive及其依賴項,包括正確的Hadoop版本。這些jar只需要出現在驅動程序上,但是如果您在yarn集羣模式下運行,那麼您必須確保它們與您的應用程序打包在一起。
spark.sql.hive.metastore.sharedPrefixes com.mysql.jdbc,org.postgresql,com.microsoft.sqlserver,oracle.jdbc 逗號分隔的類前綴列表,應使用在Spark SQL和特定版本的Hive之間共享的類加載器加載。應該共享的類的一個示例是與元存儲區對話所需的JDBC驅動程序。需要共享的其他類是與已經共享的類進行交互的類。例如,log4j使用的自定義追加程序。
spark.sql.hive.metastore.barrierPrefixes (empty) 以逗號分隔的類前綴列表,應爲Spark SQL與之通信的每個Hive版本顯式重新加載。例如,在通常會被共享的前綴中聲明的Hive UDF(即org.apache.spark.*)。

2.3.9 JDBC

物業名稱 含義
url 要連接的JDBC URL。特定於源的連接屬性可以在URL中指定。例如,jdbc:postgresql://localhost/test?user=fred&password=secret
dbtable 應該讀取的JDBC表。請注意,FROM可以使用在SQL查詢子句中有效的任何內容。例如,除了完整表之外,您還可以在括號中使用子查詢。
driver 用於連接到該URL的JDBC驅動程序的類名。
partitionColumn, lowerBound, upperBound 如果指定了這些選項,則必須全部指定。另外, numPartitions必須指定。它們描述了從多個工作程序並行讀取時如何對錶進行分區。 partitionColumn必須是相關表格中的數字列。請注意,lowerBoundupperBound僅用於確定分區的步幅,而不是用於過濾表中的行。因此,表中的所有行都將被分區並返回。此選項僅適用於閱讀。
numPartitions 表讀寫中可用於並行處理的最大分區數。這也確定了併發JDBC連接的最大數量。如果要寫入的分區數超過此限制,我們可以通過coalesce(numPartitions)在寫入之前進行調用將其降低到此限制。
fetchsize JDBC的獲取大小,它確定每次往返要獲取多少行。這可以幫助提高JDBC驅動程序的性能,該驅動程序默認爲較小的訪存大小(例如,具有10行的Oracle)。此選項僅適用於閱讀。
batchsize JDBC批處理大小,它確定每次往返要插入多少行。這可以幫助提高JDBC驅動程序的性能。此選項僅適用於寫作。默認爲1000
isolationLevel 事務隔離級別,適用於當前連接。它可以是一個NONEREAD_COMMITTEDREAD_UNCOMMITTEDREPEATABLE_READ,或SERIALIZABLE,對應於由JDBC的連接對象定義,缺省值爲標準事務隔離級別READ_UNCOMMITTED。此選項僅適用於寫作。請參閱中的文檔java.sql.Connection
sessionInitStatement 在向遠程數據庫打開每個數據庫會話之後且開始讀取數據之前,此選項將執行自定義SQL語句(或PL / SQL塊)。使用它來實現會話初始化代碼。例:option("sessionInitStatement", """BEGIN execute immediate 'alter session set "_serial_direct_read"=true'; END;""")
truncate 這是與JDBC編寫器相關的選項。當SaveMode.Overwrite啓用時,該選項的原因星火截斷,而不是刪除和重建其現有的表。這可以更有效,並防止刪除表元數據(例如索引)。但是,在某些情況下(例如,新數據具有不同的架構時),它將不起作用。默認爲false。此選項僅適用於寫作。
createTableOptions 這是與JDBC編寫器相關的選項。如果指定,則此選項允許在創建表(例如CREATE TABLE t (name string) ENGINE=InnoDB.)時設置特定於數據庫的表和分區選項。此選項僅適用於寫作。
createTableColumnTypes 創建表時要使用的數據庫列數據類型,而不是缺省值。數據類型信息應以與CREATE TABLE列語法相同的格式指定(例如:"name CHAR(64), comments VARCHAR(1024)")。指定的類型應爲有效的spark sql數據類型。此選項僅適用於寫入。
customSchema 用於從JDBC連接器讀取數據的自定義架構。例如,"id DECIMAL(38, 0), name STRING"。您還可以指定部分字段,其他部分使用默認類型映射。例如,"id DECIMAL(38, 0)"。列名應與JDBC表的相應列名相同。用戶可以指定Spark SQL的相應數據類型,而不必使用默認值。此選項僅適用於閱讀。
// Note: JDBC loading and saving can be achieved via either the load/save or jdbc methods
// Loading data from a JDBC source
val jdbcDF = spark.read
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .load()

val connectionProperties = new Properties()
connectionProperties.put("user", "username")
connectionProperties.put("password", "password")
val jdbcDF2 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)
// Specifying the custom data types of the read schema
connectionProperties.put("customSchema", "id DECIMAL(38, 0), name STRING")
val jdbcDF3 = spark.read
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

// Saving data to a JDBC source
jdbcDF.write
  .format("jdbc")
  .option("url", "jdbc:postgresql:dbserver")
  .option("dbtable", "schema.tablename")
  .option("user", "username")
  .option("password", "password")
  .save()

jdbcDF2.write
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

// Specifying create table column data types on write
jdbcDF.write
  .option("createTableColumnTypes", "name CHAR(64), comments VARCHAR(1024)")
  .jdbc("jdbc:postgresql:dbserver", "schema.tablename", connectionProperties)

2.4.0Troubleshooting(故障排除)

JDBC驅動程序類在客戶端會話和所有執行程序上必須對原始類加載器可見。這是因爲Java的DriverManager類進行了安全檢查,導致它忽略了當打開連接時原始類加載器不可見的所有驅動程序。一種方便的方法是修改所有工作程序節點上的compute_classpath.sh以包括您的驅動程序JAR。
某些數據庫(例如H2)會將所有名稱都轉換爲大寫。您需要使用大寫字母在Spark SQL中引用這些名稱。

3.sparksql性能調優

3.1在內存中緩存數據

Spark SQL可以通過調用`spark.catalog.cacheTable("tableName")`或使用內存列式格式緩存表`dataFrame.cache()`。然後,Spark SQL將僅掃描所需的列,並將自動調整壓縮以最大程度地減少內存使用和GC壓力。您可以調用`spark.catalog.uncacheTable("tableName")`從內存中刪除表。
可以使用setConf 上的方法`SparkSession`或`SET key=value`使用SQL 運行 命令來完成內存中緩存的配置。
field 默認 含義
spark.sql.inMemoryColumnarStorage.compressed 真正 設置爲true時,Spark SQL將根據數據統計信息自動爲每一列選擇一個壓縮編解碼器。
spark.sql.inMemoryColumnarStorage.batchSize 10000 控制用於列式緩存的批處理的大小。較大的批處理大小可以提高內存利用率和壓縮率,但是在緩存數據時會出現OOM。

3.2配置選項

以下選項也可以用於調整查詢執行的性能。隨着自動執行更多優化,這些選項可能會在將來的版本中被棄用。

物業名稱 默認 含義
spark.sql.files.maxPartitionBytes 134217728(128 MB) 讀取文件時打包到單個分區中的最大字節數。
spark.sql.files.openCostInBytes 4194304(4 MB) 可以同時掃描打開文件的估計成本(以字節數衡量)。將多個文件放入分區時使用。最好高估一下,然後,具有較小文件的分區將比具有較大文件的分區(首先安排)更快。
spark.sql.broadcastTimeout 300 廣播加入中廣播等待時間的秒數超時
spark.sql.autoBroadcastJoinThreshold 10485760(10 MB) 配置表的最大大小(以字節爲單位),該表在執行聯接時將廣播到所有工作程序節點。通過將此值設置爲-1,可以禁用廣播。請注意,當前僅ANALYZE TABLE COMPUTE STATISTICS noscan運行命令的Hive Metastore表支持統計信息 。
spark.sql.shuffle.partitions 200 配置在對聯接或聚集進行數據混排時要使用的分區數。

3.3SQL查詢的廣播提示

所述BROADCAST提示導向火花與另一個表或視圖接合它們時廣播的每個指定的表。當Spark確定連接方法時,即使統計數據高於配置,也首選廣播哈希連接(即BHJ)spark.sql.autoBroadcastJoinThreshold。當指定了連接的兩端時,Spark廣播統計信息較少的一方。注意Spark不能保證始終選擇BHJ,因爲並非所有情況(例如完全外部聯接)都支持BHJ。當選擇廣播嵌套循環聯接時,我們仍然遵守提示。

import org.apache.spark.sql.functions.broadcast
broadcast(spark.table("src")).join(spark.table("records"), "key").show()

3.4分佈式SQL引擎

Spark SQL還可以使用其JDBC / ODBC或命令行界面充當分佈式查詢引擎。在這種模式下,最終用戶或應用程序可以直接與Spark SQL交互以運行SQL查詢,而無需編寫任何代碼。

3.5運行Spark SQL CLI

Spark SQL CLI是在本地模式下運行Hive Metastore服務並執行從命令行輸入的查詢的便捷工具。請注意,Spark SQL CLI無法與Thrift JDBC服務器通信。

要啓動Spark SQL CLI,請在Spark目錄中運行以下命令:

./bin/spark-sql

hive的結構是通過將您做hive-site.xmlcore-site.xmlhdfs-site.xml文件conf/。您可以運行./bin/spark-sql --help以獲取所有可用選項的完整列表。

4.schema操作

schema:
Spark SQL提供的接口爲Spark提供了有關數據結構和正在執行的計算的更多信息。在內部,Spark SQL使用這些額外的信息來執行額外的優化
import spark.implicits._
// Print the schema in a tree format
df.printSchema()
// root
// |-- age: long (nullable = true)
// |-- name: string (nullable = true)

// Select only the "name" column

5.不支持的hive

hive的主要功能

帶存儲桶的表:存儲桶是Hive表分區內的哈希分區。Spark SQL還不支持存儲桶。
神祕的蜂巢功能

UNION 類型
獨特的加入
列統計信息收集:Spark SQL目前不搭載掃描來收集列統計信息,僅支持填充配置單元元存儲的sizeInBytes字段。
配置單元輸入/輸出格式

CLI的文件格式:爲了將結果顯示回CLI,Spark SQL僅支持TextOutputFormat。
Hadoop檔案
hive優化

Spark中尚未包含一些Hive優化。由於Spark SQL的內存中計算模型,其中一些(例如索引)的重要性不那麼高。其他版本已投放到Spark SQL的將來版本中。

塊級位圖索引和虛擬列(用於構建索引)
自動確定聯接和groupby的化簡器數量:當前在Spark SQL中,您需要使用“ SET spark.sql.shuffle.partitions=[num_tasks];” 控制改組後的並行度。
僅元數據查詢:對於僅可使用元數據才能回答的查詢,Spark SQL仍會啓動任務以計算結果。
偏斜數據標誌:Spark SQL不遵循Hive中的偏斜數據標誌。
STREAMTABLE連接提示:Spark SQL不遵循STREAMTABLE提示。
合併多個小文件以獲取查詢結果:如果結果輸出包含多個小文件,則Hive可以選擇將這些小文件合併爲較少的大文件,以避免HDFS元數據溢出。Spark SQL不支持該功能。
蜂巢UDF / UDTF / UDAF

Spark SQL並不支持Hive UDF / UDTF / UDAF的所有API。以下是不受支持的API:

getRequiredJars和getRequiredFiles(UDF和GenericUDF)是自動包含此UDF所需的其他資源的功能。
initialize(StructObjectInspector)GenericUDTF尚不支持in 。Spark SQL當前initialize(ObjectInspector[])僅使用不推薦使用的接口。
configure(GenericUDF,GenericUDTF和GenericUDAFEvaluator)是使用初始化函數的函數MapredContext,不適用於Spark。
close(GenericUDF和GenericUDAFEvaluator)是釋放關聯資源的功能。任務完成後,Spark SQL不會調用此函數。
reset(GenericUDAFEvaluator)是用於重新初始化聚合以重用相同聚合的函數。Spark SQL當前不支持聚合的重用。
getWindowingEvaluator(GenericUDAFEvaluator)是通過評估固定窗口上的聚合來優化聚合的功能。
不兼容的Hive UDF
以下是Hive和Spark產生不同結果的方案:

SQRT(n) 如果n <0,則Hive返回null,Spark SQL返回NaN。
ACOS(n) 如果n <-1或n> 1,則Hive返回null,Spark SQL返回NaN。
ASIN(n) 如果n <-1或n> 1,則Hive返回null,Spark SQL返回NaN。

果。
偏斜數據標誌:Spark SQL不遵循Hive中的偏斜數據標誌。
STREAMTABLE連接提示:Spark SQL不遵循STREAMTABLE提示。
合併多個小文件以獲取查詢結果:如果結果輸出包含多個小文件,則Hive可以選擇將這些小文件合併爲較少的大文件,以避免HDFS元數據溢出。Spark SQL不支持該功能。
蜂巢UDF / UDTF / UDAF

Spark SQL並不支持Hive UDF / UDTF / UDAF的所有API。以下是不受支持的API:

getRequiredJars和getRequiredFiles(UDF和GenericUDF)是自動包含此UDF所需的其他資源的功能。
initialize(StructObjectInspector)GenericUDTF尚不支持in 。Spark SQL當前initialize(ObjectInspector[])僅使用不推薦使用的接口。
configure(GenericUDF,GenericUDTF和GenericUDAFEvaluator)是使用初始化函數的函數MapredContext,不適用於Spark。
close(GenericUDF和GenericUDAFEvaluator)是釋放關聯資源的功能。任務完成後,Spark SQL不會調用此函數。
reset(GenericUDAFEvaluator)是用於重新初始化聚合以重用相同聚合的函數。Spark SQL當前不支持聚合的重用。
getWindowingEvaluator(GenericUDAFEvaluator)是通過評估固定窗口上的聚合來優化聚合的功能。
不兼容的Hive UDF
以下是Hive和Spark產生不同結果的方案:

SQRT(n) 如果n <0,則Hive返回null,Spark SQL返回NaN。
ACOS(n) 如果n <-1或n> 1,則Hive返回null,Spark SQL返回NaN。
ASIN(n) 如果n <-1或n> 1,則Hive返回null,Spark SQL返回NaN。


發佈了44 篇原創文章 · 獲贊 6 · 訪問量 2029
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章