spark結構化資料處理:Spark SQL、DataFrame和Dataset

來源:互聯網
上載者:User

標籤:

本文講解Spark的結構化資料處理,主要包括:Spark SQL、DataFrame、Dataset以及Spark SQL服務等相關內容。本文主要講解Spark 1.6.x的結構化資料處理相關東東,但因Spark發展迅速(本文的寫作時值Spark 1.6.2發布之際,並且Spark 2.0的預覽版本也發行許久),因此請隨時關注Spark SQL官方文檔以瞭解最新資訊。

文中使用Scala對Spark SQL進行講解,並且代碼大多都能在spark-shell中運行,關於這點請知曉。

概述

相比於Spark RDD API,Spark SQL包含了對結構化資料和在其上的運算的更多資訊,Spark SQL使用這些資訊進行了額外的最佳化,使對結構化資料的操作更加高效和方便。

有多種方式去使用Spark SQL,包括SQL、DataFrames API和Datasets API。但無論是哪種API或者是程式設計語言,它們都是基於同樣的執行引擎,因此你可以在不同的API之間隨意切換,它們各有各的特點,看你喜歡。

SQL

使用Spark SQL的一種方式就是通過SQL語句來執行SQL查詢。當在程式設計語言中使用SQL時,其返回結果將被封裝為一個DataFrame。

DataFrame

DataFrame是一個分布式集合,其中資料被組織為命名的列。它概念上等價於關聯式資料庫中的表,但底層做了更多的最佳化。DataFrame可以從很多資料來源構建,比如:已經存在的RDD、結構化檔案、外部資料庫、Hive表。

DataFrame的前身是SchemaRDD,從Spark 1.3.0開始SchemaRDD更名為DataFrame。與SchemaRDD的主要區別是:DataFrame不再直接繼承自RDD,而是自己實現了RDD的絕大多數功能。你仍舊可以在DataFrame上調用.rdd方法將其轉換為一個RDD。RDD可看作是分布式的對象的集合,Spark並不知道對象的詳細模式資訊,DataFrame可看作是分布式的Row對象的集合,其提供了由列組成的詳細模式資訊,使得Spark SQL可以進行某些形式的執行最佳化。DataFrame和普通的RDD的邏輯架構區別如下所示:

DataFrame不僅比RDD有更加豐富的運算元,更重要的是它可以進行執行計畫最佳化(得益於Catalyst SQL解析器),另外Tungsten項目給DataFrame的執行效率帶來了很大提升(不過Tungsten最佳化也可能在後續開發中加入到RDD API中)。

但是在有些情況下RDD可以表達的邏輯用DataFrame無法表達,所以後續提出了Dataset API,Dataset結合了RDD和DataFrame的好處。

關於Tungsten最佳化可以參見:Project Tungsten:讓Spark將硬體效能壓榨到極限

Dataset

Dataset是Spark 1.6新添加的一個實驗性介面,其目的是想結合RDD的好處(強型別(這意味著可以在編譯時間進行型別安全檢查)、可以使用強大的lambda函數)和Spark SQL的最佳化執行引擎的好處。可以從JVM物件建構出Dataset,然後使用類似於RDD的函數式轉換運算元(map/flatMap/filter等)對其進行操作。

Dataset通過Encoder實現了自訂的序列化格式,使得某些操作可以在無需解序列化的情況下直接進行。另外Dataset還進行了包括Tungsten最佳化在內的很多效能方面的最佳化。

實際上Dataset是包含了DataFrame的功能的,這樣二者就出現了很大的冗餘,故Spark 2.0將二者統一:保留Dataset API,把DataFrame表示為Dataset[Row],即Dataset的子集。

API進化

Spark在迅速的發展,從原始的RDD API,再到DataFrame API,再到Dataset的出現,速度可謂驚人,執行效能上也有了很大提升。

我們在使用API時,應該優先選擇DataFrame & Dataset,因為它的效能很好,而且以後的最佳化它都可以享受到,但是為了相容早期版本的程式,RDD API也會一直保留著。後續Spark上層的庫將全部會用 DataFrame & Dataset,比如MLlib、Streaming、Graphx等。

關於這三種API的更詳細的討論以及選擇參見:Apache Spark: RDD, DataFrame or Dataset?

入門

起點之SQLContext

要想使用Spark SQL,首先你得建立一個SQLContext對象,在這之前你只需要建立一個SparkContext就行了,如下:

另外,你也可以使用HiveContext,它是SQLContext的超集,提供了一些額外的功能:使用HiveQL解析器、訪問Hive使用者定義函數、從Hive表讀取資料。並且,你不需要安裝Hive就可以使用HiveContext。不過將來版本的Spark SQL可能會逐步縮小SQLContext和HiveContext之間的差距。

對於SQLContext,目前只有一個簡單的SQL文法解析器sql,而對於HiveContext,則可以使用hiveql和sql兩個解析器,預設是hiveql,我們可以通過如下語句來修改預設解析器:

不過就目前來說,HiveQL解析器更加完善,因此推薦使用HiveQL。

建立並使用DataFrame

通過SQLContext,應用程式可以從已經存在的RDD、結構化檔案、外部資料庫以及Hive表中建立出DataFrame。如下代碼從JSON檔案(該檔案可以從Spark發行包中找到)建立出一個DataFrame:

DataFrame提供了一個領特定領域語言(DSL)以方便操作結構化資料。下面是一些使用樣本,更多更全的DataFrame操作參見Spark API文檔中的org.apache.spark.sql.DataFrame:

另外,org.apache.spark.sql.functions單例對象還包含了一些操作DataFrame的函數,主要有這幾個方面:聚集操作、集合操作、字串處理、排序、日期計算、通用數學函數、校正碼操作、視窗操作、使用者定義函數支援等等。

在程式中執行SQL查詢

我們可以通過在程式中使用SQLContext.sql()來執行SQL查詢,結果將作為一個DataFrame返回:

我們還可以將DataFrame註冊為一個暫存資料表,然後就可以在其上執行SQL語句進行查詢了,暫存資料表的生命週期在與之關聯的SQLContext結束生命之後結束。樣本如下:

建立並使用Dataset

Dataset跟RDD相似,但是Dataset並沒有使用Java序列化庫和Kryo序列化庫,而是使用特定Encoder來序列化對象。encoder通常由代碼自動產生(在Scala中是通過隱式轉換產生),並且其序列化格式直接允許Spark執行很多操作,比如:filtering、sorting、hashing,而不需要解序列化。

與RDD互操作

Spark SQL支援兩種不同的方法將RDD轉換為DataFrame。第一種方法使用反射去推斷包含特定類型對象的RDD的模式(schema),該方法可以使你的代碼更加精簡,不過要求在你寫Spark程式時已經知道模式資訊(比如RDD中的對象是自己定義的case class類型)。第二種方法通過一個編程介面,此時你需要構造一個模式並將其應用到一個已存在的RDD以將其轉換為DataFrame,該方法適用於在運行時之前還不知道列以及列的類型的情況。

用反射推斷模式

Spark SQL的Scala介面支援將包含case class的RDD自動轉換為DataFrame。case class定義了表的模式,case class的參數名被反射讀取並成為表的列名。case class也可以嵌套或者包含複雜類型(如序列或者數組)。樣本如下:

手動編程指定模式

當case class不能提前定義時(比如記錄的結構被編碼為字串,或者當文本資料集被解析時不同使用者需要映射不同的欄位),可以通過下面三步來將RDD轉換為DataFrame:

從原始RDD建立得到一個包含Row對象的RDD。

建立一個與第1步中Row的結構相匹配的StructType,以表示模式資訊。

通過SQLContext.createDataFrame()將模式資訊應用到第1步建立的RDD上。

資料來源

DataFrame可以當作標準RDD進行操作,也可以註冊為一個暫存資料表。將DataFrame註冊為一個暫存資料表,允許你在其上執行SQL查詢。DataFrame介面可以處理多種資料來源,Spark SQL也內建支援了若干種極有用的資料來源格式(包括json、parquet和jdbc,其中parquet是預設格式)。此外,當你使用SQL查詢這些資料來源中的資料並且只用到了一部分欄位時,Spark SQL可以智能地只掃描這些用到的欄位。

通用載入/儲存函數

DataFrameReader和DataFrameWriter中包好一些通用的載入和儲存函數,所有這些操作都將parquet格式作為預設資料格式。樣本如下:

手動指定選項

你也可以手動指定資料來源和其他任何想要傳遞給資料來源的選項。指定資料來源通常需要使用資料來源全名(如org.apache.spark.sql.parquet),但對於內建資料來源,你也可以使用它們的短名(json、parquet和jdbc)。並且不同的資料來源類型之間都可以相互轉換。樣本如下:

用SQL直接查詢檔案

也可以不用read API載入一個檔案到DataFrame然後查詢它,而是直接使用SQL語句在檔案上查詢:

val df = sqlContext.sql(“SELECT * FROM parquet.`examples/src/main/resources/users.parquet`”)

儲存到持久表

可以使用DataFrameWriter.saveAsTable()將一個DataFrame儲存到一個持久化表,根據不同的設定,表可能被儲存為Hive相容格式,也可能被儲存為Spark SQL特定格式,關於這一點請參見API文檔。

我們可以通過SQLContext.table()或DataFrameReader.table()來載入一個表並返回一個DataFrame。

Parquet檔案

Parquet格式是被很多其他的資料處理系統所支援的列式資料存放區格式。它可以高效地儲存具有嵌套欄位的記錄,並支援Spark SQL的全部資料類型。Spark SQL支援在讀寫Parquet檔案時自動地儲存未經處理資料的模式資訊。出於相容性考慮,在寫Parquet檔案時,所有列將自動轉換為nullable。

載入資料

下面是SQL樣本:

分區發現

在很多系統中(如Hive),表分區是一個通用的最佳化方法。在一個分區的表中,資料通常儲存在不同的目錄中,列名和列值通常被編碼在分區目錄名中以區分不同的分區。Parquet資料來源能夠自動地發現和推斷分區資訊。 如下是人口分區表目錄結構,其中gender和country是分區列:

當使用SQLContext.read.parquet 或 SQLContext.read.load讀取path/to/table時,Spark SQL能夠自動從路徑中提取分區資訊,返回的DataFrame的模式資訊如下:

上述模式中,分區列的資料類型被自動推斷。目前,支援的資料類型有數字類型和字串類型。如果你不想資料類型被自動推斷,可以配置spark.sql.sources.partitionColumnTypeInference.enabled,預設為true,如果設定為false,將禁用自動類型推斷並預設使用字串類型。

從Spark 1.6.0開始,分區發現預設只發現給定路徑下的分區。如果使用者傳遞path/to/table/gender=male作為路徑讀取資料,gender將不被作為一個分區列。你可以在資料來源選項中設定basePath來指定分區發現應該開始的基路徑。例如,還是將path/to/table/gender=male作為資料路徑,但同時設定basePath為path/to/table/,gender將被作為分區列。

模式合并

就像ProtocolBuffer、Avro和Thrift,Parquet也支援模式演化(schema evolution)。這就意味著你可以向一個簡單的模式逐步添加列從而構建一個複雜的模式。這種方式可能導致模式資訊分散在不同的Parquet檔案中,Parquet資料來源能夠自動檢測到這種情況並且合并所有這些檔案中的模式資訊。

但是由於模式合并是相對昂貴的操作,並且絕大多數情況下不是必須的,因此從Spark 1.5.0開始預設關閉模式合并。開啟方式:在讀取Parquet檔案時,設定資料來源選項mergeSchema為true,或者設定全域的SQL選項spark.sql.parquet.mergeSchema為true。樣本如下:

JSON資料

SQLContext.read.json()可以將RDD[String]或者JSON檔案載入並轉換為一個DataFrame。

有一點需要注意的是:這裡用的JSON檔案並不是隨意的典型的JSON檔案,每一行必須是一個有效JSON對象,如果一個對象跨越多行將導致失敗。對於RDD[String]也是一樣。

下面是SQL樣本:

資料庫

Spark SQL也可以使用JDBC從其他資料庫中讀取資料,你應該優先使用它而不是JdbcRDD,因為它將返回的資料作為一個DataFrame,所以很方便操作。但請注意:這和Spark SQL JDBC服務是不同的,Spark SQL JDBC服務允許其他應用通過JDBC串連到Spark SQL進行查詢。

要在Spark SQL中串連到指定資料庫,首先需要通過環境變數SPARK_CLASSPATH設定你的資料庫的JDBC驅動的路徑。例如在spark-shell中串連MySQL資料庫,你可以使用如下命令:

可以通過SQLContext.read.format(“jdbc”).options(…).load()或SQLContext.read.jdbc(…)從資料庫中載入資料到DataFrame。如下樣本,我們從MySQL資料庫中載入資料:

下面是SQL樣本:

JDBC驅動需要在所有節點的相同路徑下都存在,因為是分散式處理嘛,就像Spark核心一樣。

有些資料庫需要使用大寫來引用相應的名字,好像Oracle中就需要使用大寫的表名。

分布式SQL引擎

Spark SQL也可以扮演一個分布式SQL引擎的角色,你可以使用JDBC/ODBC或者Spark SQL命令列介面串連到它,並直接執行互動式SQL查詢。

運行Thrift JDBC/ODBC Server

Spark SQL中實現的Thrift JDBC/ODBC Server跟Hive中的HiveServer2相一致。可以使用如下命令開啟JDBC/ODBC服務,預設情況下的服務監聽地址為localhost:10000:

這個指令碼不僅可以接受spark-submit命令可以接受的所有選項,還支援–hiveconf 屬性=值選項來配置Hive屬性。你可以執行./sbin/start-thriftserver.sh –help來查看完整的選項列表。

你可以使用beeline串連到上面已經開啟的Spark SQL引擎。命令如下:

串連到beeline需要輸入使用者名稱和密碼。在非安全模式下,簡單地輸入你自己的使用者名稱就可以了,密碼可以為空白。對於安全模式,參見beeline文檔。

運行Spark SQL CLI

Spark SQL CLI的引入使得在Spark SQL中可方便地通過Hive metastore對Hive進行查詢。目前為止還不能使用Spark SQL CLI與Thrift JDBC/ODBC Server進行互動。這個指令碼主要對本地開發比較有用,在共用的叢集上,你應該讓各使用者串連到Thrift JDBC/ODBC Server。

使用Spark SQL CLI前需要注意:

將hive-site.xml設定檔拷貝到$SPARK_HOME/conf目錄下。

需要在./conf/spark-env.sh中的SPARK_CLASSPATH添加jdbc驅動的jar包。

啟動Spark SQL CLI的命令如下:

在啟動spark-sql時,如果不指定master,則以local的方式運行,master既可以指定standalone的地址,也可以指定yarn。當設定master為yarn時(spark-sql –master yarn)時,可以通過http://master:8088 頁面監控到整個job的執行過程。如果在./conf/spark-defaults.conf中配置了spark master的話,那麼在啟動spark-sql時就不需要再指定master了。spark-sql啟動之後就可以在其中敲擊SQL語句了。

關於Spark SQL CLI的可用命令和參數,請敲擊./bin/spark-sql –help以查看。

效能調優

快取資料在記憶體中

通過調用sqlContext.cacheTable(“tableName”)或dataFrame.cache(),Spark SQL可以將表以列式儲存緩衝在記憶體中。這樣的話,Spark SQL就可以只掃描那些使用到的列,並且將自動壓縮資料以減少記憶體佔用和GC開銷。你可以調用sqlContext.uncacheTable(“tableName”)將緩衝的表從記憶體中移除。另外,你也可以在SQL/HiveQL中使用CACHE tablename和UNCACHE tablename來緩衝表和移除已緩衝的表。

和緩衝RDD時的動機一樣,如果想在同樣的資料上多次運行任務或查詢,就應該把這些資料表緩衝起來。

將資料緩衝在記憶體中,同樣支援一些選項,參見Spark SQL官方文檔效能調優部分。

其他調優相關參數

還有其他一些參數可以用於最佳化查詢效能,參見Spark SQL官方文檔效能調優部分。

 

End.

spark結構化資料處理:Spark SQL、DataFrame和Dataset

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.