關於 Apach Spark
2003—2006 年,Google 在計算機繫統領域會議 SOSP/OSDI 上發表了 Google File System、MapReduce、BigTable 3 篇重量級的繫統論文,分別討論了大規模數據如何存儲、處理及結構化組織。之後,Apache Hadoop 社區對這些論文進行了開源實現,開發了HDFS 分布式文件繫統、Hadoop MapReduce 大數據處理框架和 HBase 分布式 Key-Value數據庫,大大降低了大數據應用開發、運行及數據存儲管理的難度。這些繫統被廣泛應用於互聯網、電信、金融、科研等領域,以進行大規模數據存儲與處理。對於大數據處理框架來說,MapReduce 成功地將函數式編程思想引入分布式數據處理中,僅僅用兩個函數(map() 和 reduce())就解決了一大類的大數據處理問題,而且不要求用戶熟悉分布式文件繫統。然而,隨著大數據應用越來越多,處理性能的要求越來越高,MapReduce 的一些缺點也顯現出來。例如,MapReduce 編程模型的表達能力較弱,僅使用map() 和 reduce() 兩個函數難以實現復雜的數據操作;處理流程固定,不容易實現迭代計算;基於磁盤進行數據傳遞,效率較低。這些缺點導致使用 MapReduce 開發效率較低、執行復雜的數據處理任務的性能也不高。
為了解決這些問題,微軟在 2008—2009 年研發了 Dryad/DryadLINQ,其中 Dryad 類 似 MapReduce,但以有向無環圖(Directed Acycline Graph,DAG)形式的數據流取代了MapReduce 固定的 map-reduce 兩階段數據流,處理流程更通用,支持復雜的數據處理任務。DryadLINQ 為 Dryad 提供了高層編程語言,將更多的函數式編程思想(來源於 C# 的LINQ)引入編程模型中,表達能力更強,如容易實現 join() 等操作。然而,由於 Dryad/DryadLINQ 在當時沒有開源,所以沒有得到大規模使用。鋻於 MapReduce、Dryad 等框架存在一些問題,2012 年,UC Berkeley 的 AMPLab 研發並開源了新的大數據處理框架 Spark。其核心思想包括兩方面:一方面對大數據處理框架的輸入 / 輸出、中間數據進行建模,將這些數據抽像為統一的數據結構,命名為彈性分布式數據集(Resilient Distributed Dataset,RDD),並在此數據結構上構建了一繫列通用
的數據操作,使得用戶可以簡單地實現復雜的數據處理流程;另一方面采用基於內存的數據聚合、數據緩存等機制來加速應用執行,尤其適用於迭代和交互式應用。Spark 采用EPFL 大學研發的函數式編程語言 Scala 實現,並且提供了 Scala、Java、Python、R 四種語言的接口,以方便開發者使用熟悉的語言進行大數據應用開發。
經過多年的發展,Spark 也與 Hadoop 一樣構建了完整的生態繫統。Apache Spark 生態繫統以 Spark 處理框架為核心,在上層構建了面向 SQL 語言的 Spark SQL 框架、面向大規模圖計算的 GraphX 框架、面向大規模機器學習的 MLlib 框架及算法庫,以及面向流處理的 Spark Streaming 框架;在下層,Spark 及其關聯社區也推出了相關存儲繫統,如基於內存的分布式文件繫統 Alluxio、支持 ACID 事務的數據湖繫統 Delta Lake 等。由於整個Spark 生態繫統中包含的繫統和框架眾多,本書主要關注生態繫統中核心的 Spark 處理框架本身。下面介紹本書的一些基本信息。
本書的寫作目的及面向的讀者
本書的寫作目的是以 Spark 框架為核心,總結大數據處理框架的設計和實現原理,包括框架設計中面臨的基本問題、可能的解決方案、Spark 采用的方案、具體實現方法,以及優缺點等。與機器學習等領域有成熟的理論模型不同,大數據處理框架並沒有一個完整的理論模型,但是具有一些繫統設計模型及設計方案,如編程模型、邏輯處理流程、物理執行計劃及並行化方案等。這些模型及方案是研究人員和工程師在實踐中經過不斷探索和改進得到的實踐經驗。本書的目的是將這些寶貴經驗抽像總結為繫統設計模型和方案,幫助讀者從理論層和實現層深入理解大數據處理框架,具體體現如下。
(1)幫助讀者理解 Spark 應用,以及與 Spark 關聯的上下層框架。Spark 等大數據處理框架隻將數據接口和操作接口暴露給用戶,用戶一般不了解應用程序是如何被轉化為可分布執行任務的,以及任務是如何執行的。本書詳細總結了 Spark 框架將應用程序轉化為邏輯處理流程,並進一步轉化為物理執行計劃的一般過程。理解這個過程將幫助讀者理解更復雜的 Spark 應用,如在第 5 章中介紹的迭代型機器學習應用和圖計算應用。同時,理解 Spark 的設計與實現原理也有助於理解 Spark 生態圈中的上下層框架之間的關繫,如Spark SQL、MLlib、GraphX 是如何利用 Spark 框架來執行 SQL、機器學習和圖計算任務的。
(2)幫助讀者開發性能更好、可靠性更高的大數據應用。用戶在運行大數據應用時,經常面臨應用執行效率低下、無響應、I/O 異常、內存溢出等性能和可靠性問題。本書講述了與 Spark 框架性能和可靠性相關的 Shuffle 機制、數據緩存機制、錯誤容忍機制、內存管理機制等。理解 Spark 應用性能和可靠性的影響因素,幫助讀者在運行 Spark 應用時進行參數調優,有助於更好地利用數據緩存來提升應用性能,同時合理利用數據持久化機制來減少執行錯誤、內存溢出等可靠性問題。
(3)幫助讀者對 Spark 等大數據框架進行進一步優化和改進。在實際使用大數據處理框架的過程中,開發者經常會因為軟硬件環境、數據特征、應用邏輯、執行性能的特殊需求,需要對 Spark 框架的部分功能進行優化改進。例如,在內存較小的集群上運行需要對內存管理進行改進,經常出現網絡阻塞時需要對 Shuffle 機制進行改進,針對一些特殊應用需要開發新的數據操作等。要對 Spark 框架進行改進,不僅需要非常了解 Spark 框架的設計和實現原理,還需要清楚改進後可能出現的正確性和可靠性問題。本書對 Spark 設計和實現過程中的問題挑戰、解決方案、優缺點的闡述將幫助開發者和研究人員對框架進行優化改進。
因此,我們相信,本書對於大數據處理框架的用戶、開發者和研究人員都會有一定的幫助。
本書的主要內容
本書主要介紹 Apache Spark 大數據處理框架的設計和實現原理,附帶討論與 Hadoop MapReduce 等框架的優缺點對比。全書分 9 章,主要包含以下四部分內容。第一部分 大數據處理框架的基礎知識。第 1 章介紹大數據處理框架的基本概念、繫統架構,以及與其相關的研究工作。第 2 章概覽 Spark 的繫統架構、編程模型,並以一個典型的 Spark 應用為例概述 Spark 應用的執行流程。Spark 初學者可以直接從第 2 章開始閱讀。
第二部分 Spark 大數據處理框架的核心理論。該部分包括兩章,主要介紹 Spark 如何將應用程序轉化為可以分布執行的計算任務。其中,第 3 章介紹 Spark 將應用程序轉化為邏輯處理流程的一般過程及方法,並對常用的數據操作進行詳細分析。第 4 章討論 Spark將邏輯處理流程轉化為物理執行計劃的一般過程及方法,以及常用的數據操作形成的計算任務。
第三部分 典型的 Spark 應用。第 5 章介紹復雜的迭代型 Spark 應用,包括迭代型機器學習應用和圖計算應用,以及這些應用的並行化方法。這些應用也進一步驗證了 Spark框架的通用性。
第四部分 大數據處理框架性能和可靠性保障機制。該部分主要探究 Spark 框架性能和可靠性相關的技術,其中包括第 6 章的 Shuffle 機制、第 7 章的數據緩存機制、第 8 章的錯誤容忍機制及第 9 章的內存管理機制。
本書特點
本書注重設計原理和實現方案的闡述,寫作方式考慮了技術深度、研究價值、易讀性等因素,具體特點如下。
(1)采用問題驅動的闡述方式。本書在章節開頭或者中間引入 Spark 設計和實現過程中面臨的挑戰性問題(Problems),然後將這些問題拆分為子問題逐步深入討論可能的解決方案(Potential Solutions),最後介紹為什麼 Spark 會使用當前的解決方案(Why)及具體是如何實現的(How)。這種表述方式可以讓讀者“知其然”,並且“知其所以然”,也就是讓讀者既從使用者角度又從設計者角度來理解大數據處理框架中的基本問題、基本設計方法及實現思想。
(2)強調基本原理的闡述。本書著重介紹 Spark 設計和實現的基本原理,不具體展示代碼實現的細節。這樣做的第一個原因是,其基本原理是對代碼的高層抽像,對大數據框架的用戶、開發者和研究人員來說,原理更重要、更容易理解。第二個原因是,代碼在不斷優化更新,而基本原理比較穩定。本書也可以看作從 Spark 代碼中抽像出概要設計和詳細設計。
(3)圖文並茂,容易理解。本書將復雜的設計和實現邏輯抽像為圖例,並通過文字描述和實例來方便讀者理解復雜的設計方案和執行過程。對於一些復雜的問題,本書還進行了分類討論,使技術內容更具有邏輯性、更容易被理解。
(4)具有一定的學術研究價值。本書討論了 Spark 設計與實現過程中存在的一些挑戰性問題及當前解決方案的不足,同時也探討了一些相關的研究工作,並在必要時與Hadoop 進行了對比。這些討論將有助於讀者研究和優化改進大數據處理框架。
Spark 版本與 API 問題
本書以 Spark 2.4.3 版和 RDD API 為基礎進行編寫。實際上,從 Spark 2.0 版到未來的Spark 3.0 版,Spark 社區推薦用戶使用 DataSet、DataFrame 等面向結構化數據的高層 API(Structured API)來替代底層的 RDD API,因為這些高層 API 包含更多的數據類型信息(Schema),支持 SQL 操作,並且可以利用經過高度優化的 Spark SQL 引擎來執行。然而,由於以下幾個原因,本書使用 RDD API 來探討 Spark 的設計原理和實現。
(1)RDD API 更基礎,更適合分析大數據處理框架中的一些基本問題和原理。相比DataSet、DataFrame 數據結構,RDD API 的數據結構更簡單和通用,更適合用來展示一些基本概念和原理,如數據分區方法、每個數據操作的執行過程(生成什麼樣的 RDD API、建立什麼樣的數據依賴關繫),以及執行階段劃分方法、任務劃分方法等。而且,RDD API 包含更多的數據操作類型,可以用來展示更豐富的數據依賴關繫、邏輯處理流程等。
(2)學習 RDD API 及其執行原理,幫助讀者理解高層 API 和 Spark SQL 執行引擎。由於 Spark SQL 執行引擎采用了許多數據庫、分布式繫統、編譯等領域的優化技術,其將應用程序(使用 DataSet/DataFrame API 編寫)轉化為物理執行計劃的過程比較復雜。本書講解了“RDD API 應用程序-邏輯處理流程-物理執行計劃”的基本轉化過程,幫助讀者進一步學習和理解更復雜的 Spark SQL 轉化過程。讀者可以參閱 Bill Chambers 和 Matei Zaharia 合著的 Spark: The Definitive Guide,學習 DataSet、DataFrame API 的具體使用方法,也可以參閱朱鋒、張韶全、黃明合著的《Spark SQL 內核剖析》,進一步學習 Spark SQL引擎的執行過程和具體實現。
(3)上層框架(如 MLlib、GraphX)中有很多代碼使用 RDD API,學習原生的 RDD API 有助於在分布式層面理解數據和計算的抽像表達,也有助於理解圖計算應用和機器學習應用中的數據分區(如邊劃分、點劃分),以及計算任務劃分方法(如數據並行等)。
另外,在未來的 Spark 3.0 版本中還會有一些新的特性,如支持 GPU 調度、SQL 自適大數據處理框架應查詢執行等。如果有機會,我們會在本書的下一版中探討更多關於 Spark SQL 執行引擎的設計原理,以及這些新特性。
與 SparkInternals 技術文檔的關繫
有些讀者可能看過我們在 2015 年撰寫並在 GitHub 上公開的 SparkInternals 技術文檔。該文檔介紹了 Spark 1.0 與 Spark 2.0 版本的設計原理和實現,迄今已收到 4000 多顆 stars、1500 次 forks,也被翻譯為英文、日文和泰文,受到很多大數據工程師和研究人員的關注。
不過,SparkInternals 技術文檔中總結的設計原理不夠完整和深入,而且後幾章中涉及的實現細節也較多(包含一些實現細節代碼)。由於這本書的目的是總結設計原理和實現,所以並沒有使用太多 SparkInternals 技術文檔中的內容,而是按照“問題-解決方案- Spark采用的方案-實現-優缺點”的邏輯重新撰寫了相關章節,隻是使用了 SparkInternals 技術文檔中的部分圖例。當然,如果讀者想要了解一些實現細節和代碼,也可將 SparkInternals技術文檔作為本書的補充資料。
我們在 GitHub 上建立了一個名為 ApacheSparkBook 的公開項目,將本書設計的示例代碼和高清圖片放到了項目中。
讀者可以在項目中進行提問,方便我們解答問題或勘誤。由於作者水平和經驗有限,本書的錯漏和不足之處歡迎廣大讀者朋友批評指正,可以將意見提交到 GitHub 項目中或者通過電子郵件聯繫作者。在本書寫作過程中,作者得到了所在單位(中國科學院軟件研究所)諸多老師的關注和支持,在此感謝魏峻、武延軍、王偉等老師提供的科研工作環境。作者的研究工作受到國家自然科學基金(61802377),以及中國科學院青年創新促進會的項目支持。
感謝參與本書初稿討論和審閱的各位朋友,包括亞馬遜的紀樹平博士、騰訊的朱鋒博士、阿裡巴巴的瀋雯婷、中國科學院軟件研究所的李慧、王棟、葉星肜、康鍇等同學,以及 Databricks 的 Xiao Li 博士及其團隊。同時,也感謝廣大讀者對 GitHub 上 SparkInternals技術文檔的支持和反饋意見。
電子工業出版社的孫學瑛編輯及其團隊在本書的審校、排版、出版發行過程中付出了巨大努力,在此表示由衷感謝!
最後,感謝家人及朋友對作者一如既往的支持。