Spark SQL 教程: 通過示例瞭解 Spark SQL

banq發表於2021-12-29

Apache Spark 是一個閃電般的叢集計算框架,專為快速計算而設計。隨著大資料生態系統中實時處理框架的出現,公司在其解決方案中嚴格使用 Apache Spark。Spark SQL 是 Spark 中的一個新模組,它將關係處理與 Spark 的函數語言程式設計 API 整合在一起。它支援通過 SQL 或 Hive 查詢語言查詢資料。通過這篇部落格,我將向您介紹 Spark SQL 這個令人興奮的新領域。

 

什麼是 Spark SQL?

Spark SQL 將關係處理與 Spark 的函數語言程式設計相結合。它提供對各種資料來源的支援,並使可以將 SQL 查詢與程式碼轉換編織在一起,從而產生一個非常強大的工具。

 

為什麼使用 Spark SQL?

Spark SQL 起源於 Apache Hive,執行在 Spark 之上,現在與 Spark 堆疊整合。Apache Hive 有如下所述的某些限制。Spark SQL 旨在克服這些缺點並取代 Apache Hive。

 

Spark SQL 比 Hive 更快嗎?

在處理速度方面,Spark SQL 比 Hive 更快。下面我列出了 Hive 對 Spark SQL 的一些限制。

Hive 的限制:

  • Hive 在內部啟動 MapReduce 作業以執行即席查詢。 在分析中型資料集(10 到 200 GB)時,MapReduce 的效能滯後。
  • Hive 沒有恢復功能。這意味著,如果處理在工作流程中間終止,您將無法從卡住的地方恢復。
  • 當垃圾被啟用並導致執行錯誤時,Hive 無法級聯刪除加密的資料庫。為了克服這個問題,使用者必須使用清除選項來跳過垃圾而不是丟棄。 

這些缺點讓位於 Spark SQL 的誕生。但仍然存在於我們大多數人腦海中的問題是:

  • Spark SQL 是資料庫嗎?

Spark SQL 不是一個資料庫,而是一個用於結構化資料處理的模組。它主要適用於作為程式設計抽象的 DataFrames,通常充當分散式 SQL 查詢引擎。

  • Spark SQL 是如何工作的?

讓我們探索一下 Spark SQL 必須提供的功能。Spark SQL 模糊了 RDD 和關係表之間的界限。通過與 Spark 程式碼整合的宣告性 DataFrame API,它在關係處理和過程處理之間提供了更緊密的整合。它還提供了更高的優化。DataFrame API 和 Datasets API 是與 Spark SQL 互動的方式。

藉助 Spark SQL,更多使用者可以訪問 Apache Spark,並改進了對當前使用者的優化。 Spark SQL 提供了 DataFrame API,可以對外部資料來源和 Spark 的內建分散式集合執行關係操作。它引入了一個名為 Catalyst 的可擴充套件優化器,因為它有助於支援大資料中的各種資料來源和演算法。

Spark 可以在 Windows 和類 UNIX 系統(例如 Linux、Microsoft、Mac OS)上執行。在一臺機器上本地執行很容易——你所需要的只是在你的系統PATH上安裝java ,或者JAVA_HOME環境變數指向 Java 安裝。

 

Spark SQL 庫

Spark SQL 有以下四個庫,用於與關係和過程處理互動:

1.資料來源API(應用程式介面):

這是一個用於載入和儲存結構化資料的通用 API。

  • 它內建了對 Hive、Avro、JSON、JDBC、Parquet 等的支援。
  • 支援通過 Spark 包進行第三方整合
  • 支援智慧來源。
  • 它是一種適用於結構化和半結構化資料的資料抽象和領域特定語言 (DSL)。
  • DataFrame API 是以命名的列和行的形式分佈的資料集合。
  • 它像 Apache Spark Transformations 一樣被惰性評估,並且可以通過 SQL Context 和 Hive Context 訪問。
  • 它在單節點叢集到多節點叢集上處理千位元組到拍位元組大小的資料。
  • 支援不同的資料格式(Avro、CSV、Elastic Search和 Cassandra)和儲存系統(HDFS、HIVE Tables、MySQL等)。
  • 可以通過 Spark-Core 與所有大資料工具和框架輕鬆整合。
  • 為Python、Java、Scala和R 程式設計提供 API 。

2.資料幀API:

DataFrame 是組織成命名列的分散式資料集合。 它 相當於 SQL 中的關係表, 用於將資料儲存到表中。

3. SQL 直譯器和優化器:

SQL 直譯器和優化器基於在 Scala 中構建的函數語言程式設計。

  • 它是 SparkSQL 中最新、技術最先進的元件。 
  • 它提供了轉換樹的通用框架,用於執行分析/評估、優化、規劃和執行時程式碼生成。
  • 這支援基於成本的優化(執行時間和資源利用率稱為成本)和基於規則的優化,使查詢執行速度比其 RDD(彈性分散式資料集)對應物快得多。

     例如,Catalyst 是一個模組化庫,它是作為基於規則的系統而製作的。框架中的每條規則都側重於不同的優化。

4、SQL服務:

SQL 服務是在 Spark 中處理結構化資料的入口點。它允許建立DataFrame物件以及執行 SQL 查詢。

  

Spark SQL 的特點

以下是 Spark SQL 的特點:

  1. 與 Spark 整合Spark SQL 查詢與 Spark 程式整合。Spark SQL 允許我們使用 SQL 或可在 Java、Scala、Python 和 R 中使用的 DataFrame API 查詢 Spark 程式中的結構化資料。要執行流式計算,開發人員只需針對 DataFrame / Dataset API 編寫批處理計算, Spark 會自動增加計算量,以流式方式執行它。這種強大的設計意味著開發人員不必手動管理狀態、故障或保持應用程式與批處理作業同步。相反,流式作業總是在相同資料上給出與批處理作業相同的答案。
  2. 統一資料訪問DataFrames 和 SQL 支援訪問各種資料來源的通用方法,如 Hive、Avro、Parquet、ORC、JSON 和 JDBC。這將連線這些來源的資料。這對於將所有現有使用者容納到 Spark SQL 中非常有幫助。
  3. 蜂巢相容性Spark SQL 對當前資料執行未經修改的 Hive 查詢。它重寫了 Hive 前端和元儲存,允許與當前的 Hive 資料、查詢和 UDF 完全相容。
  4. 標準連線連線是通過 JDBC 或 ODBC 進行的。JDBC和 ODBC 是商業智慧工具連線的行業規範。
  5. 效能和可擴充套件性Spark SQL 結合了基於成本的優化器、程式碼生成和列式儲存,使用 Spark 引擎在計算數千個節點的同時使查詢變得敏捷,提供完整的中間查詢容錯。Spark SQL 提供的介面為 Spark 提供了有關資料結構和正在執行的計算的更多資訊。在內部,Spark SQL 使用此額外資訊來執行額外優化。Spark SQL 可以直接從多個來源(檔案、HDFS、JSON/Parquet 檔案、現有 RDD、Hive 等)讀取。它確保現有 Hive 查詢的快速執行。Spark SQL 的執行速度比 Hadoop 快 100 倍。

 

使用者定義函式Spark SQL 具有語言整合的使用者定義函式 (UDF)

UDF 是 Spark SQL 的一個特性,用於定義新的基於列的函式,這些函式擴充套件了 Spark SQL 的 DSL 詞彙表,用於轉換資料集。UDF 在執行過程中是黑匣子。

下面的示例定義了一個 UDF,用於將給定的文字轉換為大寫。

程式碼說明:

1. 建立資料集“hello world”

2. 定義一個將字串轉換為大寫的函式“upper”。

3. 我們現在將 'udf' 包匯入 Spark。

4. 定義我們的 UDF,'upperUDF' 並匯入我們的函式 'upper'。

5. 在新的“upper”列中顯示我們的使用者定義函式的結果。

val dataset = Seq((0, "hello"),(1, "world")).toDF("id","text")
val upper: String => String =_.toUpperCase
import org.apache.spark.sql.functions.udf
val upperUDF = udf(upper)
dataset.withColumn("upper", upperUDF('text)).show

程式碼說明:

1. 我們現在將我們的函式註冊為“myUpper”

2. 在其他函式中編目我們的 UDF。

spark.udf.register("myUpper", (input:String) => input.toUpperCase)
spark.catalog.listFunctions.filter('name like "%upper%").show(false)

 

使用 Spark SQL 查詢

我們現在將開始使用 Spark SQL 進行查詢。請注意,實際的 SQL 查詢類似於流行的 SQL 客戶端中使用的查詢。

啟動 Spark Shell。進入 Spark 目錄,在終端執行 ./bin/spark-shell 成為 Spark Shell。

對於部落格中顯示的查詢示例,我們將使用兩個檔案,“employee.txt”和“employee.json”。這兩個檔案都儲存在包含 Spark 安裝 (~/Downloads/spark-2.0.2-bin-hadoop2 .7)。因此,所有正在執行查詢的人,請將它們放在此目錄中或在下面的程式碼行中設定檔案的路徑。

程式碼說明:

1、我們先匯入一個Spark Session到Apache Spark中。

2. 使用 'builder()' 函式建立 Spark 會話 'spark'。

3. 將 Implicts 類匯入到我們的“spark”會話中。

4. 我們現在建立一個 DataFrame 'df' 並從 'employee.json' 檔案匯入資料。

5. 顯示資料幀“df”。結果是我們的“employee.json”檔案中包含 5 行年齡和姓名的表格。 

import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder().appName("Spark SQL basic example").config("spark.some.config.option", "some-value").getOrCreate()
import spark.implicits._
val df = spark.read.json("examples/src/main/resources/employee.json")
df.show()

程式碼說明:

1. 將 Implicts 類匯入到我們的“spark”會話中。

2. 列印我們的 'df' DataFrame 的架構

3. 顯示來自“df”DataFrame 的所有記錄的名稱。

import spark.implicits._
df.printSchema()
df.select("name").show()

程式碼說明:

1. 顯示每個人的年齡增加兩年後的DataFrame。

2. 我們篩選出所有 30 歲以上的員工並顯示結果。

df.select($"name", $"age" + 2).show()
df.filter($"age" > 30).show()

程式碼說明:

1. 統計年齡相同的人數。我們同樣使用“groupBy”函式。

2. 建立我們的 'df' DataFrame 的臨時檢視 'employee'。

3. 在我們的'employee' 檢視上執行'select' 操作以將表顯示為'sqlDF'。

4.顯示'sqlDF'的結果。

df.groupBy("age").count().show()
df.createOrReplaceTempView("employee")
val sqlDF = spark.sql("SELECT * FROM employee")
sqlDF.show()
 

 

建立資料集

在瞭解了 DataFrames 之後,讓我們現在轉到 Dataset API。下面的程式碼在 SparkSQL 中建立了一個 Dataset 類。

程式碼說明:

1. 建立一個“Employee”類來儲存員工的姓名和年齡。

2. 分配一個資料集“caseClassDS”來儲存安德魯的記錄。

3. 顯示資料集“caseClassDS”。

4. 建立一個原始資料集來演示資料幀到資料集的對映。

5. 將上述序列賦值給一個陣列。

case class Employee(name: String, age: Long)
val caseClassDS = Seq(Employee("Andrew", 55)).toDS()
caseClassDS.show()
val primitiveDS = Seq(1, 2, 3).toDS
()primitiveDS.map(_ + 1).collect()

程式碼說明:

1.設定我們的JSON檔案'employee.json'的路徑。

2. 從檔案建立資料集。

3. 顯示'employeeDS' Dataset 的內容。

val path = "examples/src/main/resources/employee.json"
val employeeDS = spark.read.json(path).as[Employee]
employeeDS.show()
 

將模式新增到 RDD

Spark 引入了 RDD(彈性分散式資料集)的概念,這是一個不可變的容錯分散式物件集合,可以並行操作。RDD 可以包含任何型別的物件,它是通過載入外部資料集或從驅動程式分發集合來建立的。

Schema RDD 是一個可以執行 SQL 的 RDD。它不僅僅是 SQL。它是結構化資料的統一介面。

程式碼說明:

1. 為RDDs匯入Expression Encoder。RDD 類似於資料集,但使用編碼器進行序列化。

2. 將Encoder 庫匯入shell。

3. 將 Implicts 類匯入到我們的“spark”會話中。

4. 從“employee.txt”建立一個“employeeDF”DataFrame,並將基於分隔符逗號“,”的列對映到臨時檢視“employee”中。

5. 建立臨時檢視“員工”。

6. 定義一個 DataFrame 'youngstersDF',它將包含所有年齡在 18 到 30 歲之間的員工。

7. 將來自 RDD 的名字對映到'youngstersDF'以顯示年輕人的名字。

import org.apache.spark.sql.catalyst.encoders.ExpressionEncoder
import org.apache.spark.sql.Encoder
import spark.implicits._
val employeeDF = spark.sparkContext.textFile("examples/src/main/resources/employee.txt").map(_.split(",")).map(attributes => Employee(attributes(0), attributes(1).trim.toInt)).toDF()
employeeDF.createOrReplaceTempView("employee")
val youngstersDF = spark.sql("SELECT name, age FROM employee WHERE age BETWEEN 18 AND 30")
youngstersDF.map(youngster => "Name: " + youngster(0)).show()

程式碼說明:

1. 將對映名稱轉換為字串進行轉換。

2. 使用 Implicits 類中的 mapEncoder 將名稱對映到年齡。

3. 將名稱對映到我們的 'youngstersDF' DataFrame 的年齡。結果是一個名稱對映到各自年齡的陣列。

youngstersDF.map(youngster => "Name: " + youngster.getAs[String]("name")).show()
implicit val mapEncoder = org.apache.spark.sql.Encoders.kryo[Map[String, Any]]
youngstersDF.map(youngster => youngster.getValuesMap[Any](List("name", "age"))).collect()

更多點選標題

 

相關文章