《使用Python和Dask實現分布式並行計算》1. Why scalable computing matters(為什么可伸縮計算很重要)


楔子

開新坑啦,最新本人發現了一本書叫《Data Science with Python and Dask》,或許你還不知道它是什么,或許你已經對它有所了解、但是了解的不夠深入。如果是這樣的話,那么讓我們從頭開始一起學習吧。

這本書是英文版本的,所以個人決定將它從頭到尾翻譯一遍,當然我也會加入一些個人的想法進去,以及示例所使用的代碼和書中也不一定是一樣的。不過我相信這些都不是問題,你在學習的時候編寫的代碼也不一定要和我一樣。

我當前使用操作系統是Windows,Python版本是3.8.1。

廢話不多說,下面就開始吧。


歡迎來到《Data Science with Python and Dask》,既然你選擇了這本書,那么毫無疑問你肯定對數據科學和機器學習感興趣,也許你已經是一名數據科學家、分析師,或者機器學習工程師。然而,我猜測你現在正面臨(或者曾經面臨過)一個巨大的挑戰,沒錯,就是當你在處理大型數據集時所面臨的那些眾所周知的挑戰。比如:即使是執行非常簡單的計算,也需要花費漫長的時間;數據集過大使得單機無法全量讀取,進而導致無法像平常那樣編寫代碼,需要思考很多額外的東西,比如:數據集的切分等等。當然還有其它的挑戰,就不一一列舉了。

但是不要絕望,隨着大型數據集在收集和存儲方面所需要的費用和工作量的顯著下降,這些挑戰早已變得司空見慣。作為回應,計算機科學社區已經投入了大量的精力去創建一個更好、更容易使用的編程框架,來降低處理海量數據的復雜性。目前已經出現了很多不同的技術和框架,都在致力於解決這些問題,比如:Dask。在功能和靈活性方面,Dask可以說是出類拔萃,在眾多框架中處於領先水平。而這本書的目的就是教你如何使用Dask來對大型數據集進行分析和建模,從而提升你的數據科學水平,引領你走向數據科學中的下一個高峰。

誰適合這本書?誰不適合這本書?

值得注意的是,Dask可以解決的問題范圍很廣,其中包括但不限於:數據的結構化分析、科學計算中的大型仿真,以及通用分布式計算。所以Dask的能力是獨一無二的,它可以處理許多種類的問題,如果我們試圖覆蓋Dask的每一個方方面面,那么這本書將會灰常灰常的厚。因此在本書中,我們只將目光聚焦於使用Dask進行數據分析和機器學習,盡管也會涉及Dask的一些其它方面,但這不是我們的重點。

這本書是為初級數據科學家、數據工程師,以及分析人員編寫的,特別是那些還不知如何處理超過單機極限數據量的人。我們將會覆蓋數據科學的絕大部分范圍,從數據准備到使用Dask進行分析和模型構建,深刻體會分布式計算的基本原理。

如果你使用過諸如spark之類的分布式計算框架,或者你已經熟練掌握numpy、scipy、pandas技術棧,那么你會發現這本書不太適合你,因為Dask設計之初就是為了更輕松地擴展numpy和pandas,所以你可以更好的利用其它資源,比如:API文檔。不過即便如此,這本書還是會給你帶來一些其它的驚喜的。(突然間不想翻譯了,怎么破?)

這本書主要圍繞着作為一名數據科學家或數據分析師在工作中都會遇到的典型任務,以及相關的處理方式,但是在這一章我們會先介紹一些基礎知識,來理解Dask的工作原理。首先我們會研究像Dask這樣的工具為什么在你的數據科學工具箱中是必不可少的,以及它的獨特之處;然后我們將介紹有向無環圖,因為Dask廣泛使用這個概念來控制代碼的並行執行;有了這些知識,我們將會更好地理解Dask在處理大型數據集時是如何工作的。當你不斷深入Dask時,這些知識將會很好的為你服務,並且在后續章節中我們還會討論如何在雲端搭建自己的集群,那時我們還會回顧這些知識。那么現在就讓我們將目光轉向Dask的獨特之處,並分析它為什么是數據科學中的一個有力工具。

為什么是Dask?

對於現代組織而言,數據科學的發展前景是普遍具有吸引力的,並且具備足夠充分的理由。另一方面,高效的數據科學團隊可以將單純的0和1轉換為真正的競爭優勢,比如:做出更好的決策、優化業務流程,以及檢測戰略盲點,這些都是投資數據科學能力所帶來的好處。然而我們今天所談論的數據科學並不是一個真正的新概念,因為在過去的幾十年里,世界各地的組織都在努力尋找更好的方法來做出戰略和戰術決策,盡管使用的手段各不相同,比如有:決策支持、商業智能、分析或簡單的運籌學等等,但是目的都是一致的,那就是:密切關注正在發生的事情,做出更明智的選擇。然而近年來發生了很大的變化,學習和使用數據科學的障礙已經大大降低,數據科學不再只局限於運籌學期刊或大型咨詢集團的數據研究和開發部門。而將數據科學帶給大眾的一個關鍵角色便是Python編程語言、以及它的一些被稱為"Python開源數據科學技術棧"的第三方模塊。可以說Python在數據科學領域的一些第三方庫是使得Python在現如今變得如此流行的最大功臣,這些第三庫顯然我們都是熟知的,比如:numpy、pandas、scipy、scikit-learn等等,這些庫已經成為了一個工業標准,社區擁有大量的開發人員以及豐富的學習資料。而其它的語言也有這樣的優勢,比如:matlab、fortran等等,但是由於學習起來比較困難,沒有那么多的社區支持,由於這些原因,Python和它的一些第三方庫已經成為學習數據科學、以及進行相關開發的最流行的平台之一。

隨着數據科學的發展,計算機也變得越來越強大,這使得生產、收集、存儲和處理數據變得容易,並且支持的數據量也更多。但是現在大量的數據出現,已經讓許多組織開始質疑,將所有的數據都收集和存儲起來究竟有沒有價值?這個想法是正確的,因為原始數據沒有內在價值,必須對其進行清理、檢測和分析,才能從中提取可操作的信息,進而將其變成價值。而這一步便是數據科學家,也就是屏幕前的你發揮作用的地方。通過Python,數據科學家可以很輕松的使用pandas進行數據清理和探索式數據分析,使用numpy和scipy對數據進行統計測試,使用scikit-learn構建預測模型,當然還可以使用matplotlib、pyecharts、plotly、bokeh進行數據可視化。這一切都是那么的完美,真的是從頭到尾都有現成的工具讓你用,但是顯然我們忽略了一個問題。我們能夠如此輕松的處理,最大的一個前提就是這些數據可以被單機讀取,可在現如今數據量爆炸的時代,單機顯然是解決不了問題的。像阿里巴巴等電商,早在十幾年前就遇見了這種問題,當時淘寶人數的大量增加以及分布式技術的不成熟使得它們的業務受到了很大的制約。因此當使用超過一定大小的數據集時,這些工具的可行性就出現了上限,而一旦越過了這個閾值,本章所描述的問題便會開始出現。

但是數據量多大才算大呢?我們經常說大數據,多大的數據量才能被稱之為大數據呢?有沒有相應的門檻規定呢?因此為了避免定義不明確,加之大數據這個術語已經被過度使用,我們在這里有必要聲明一下。在本系列中,我們將數據集分為三種,分別是:小型數據集、中型數據集、大型數據集,其標准如下。

小型數據集的特點是可以輕松地讀取到內存中,並且還可以留出足夠的空間進行操作和轉換,它們通常不超過2-4GB,像排序和聚合這樣的復雜操作也可以在不分頁的情況下完成。提到分頁,那么什么是分頁呢?分頁指的是在計算的過程中,使用計算機持久化存儲(機械硬盤或固態硬盤)來保存中間的計算結果。所以當內存不夠的時候,使用硬盤來保存臨時結果便是分頁,因為我們在計算的過程中肯定會產生大量的臨時結果,如果內存不夠的話,便會將臨時結果通過硬盤來進行存儲。而且一旦涉及到分頁,那么存儲和讀取的速度會大幅度降低,因為涉及到了磁盤,而spark之所以比Hadoop的MapReduce快那么多,也是由於Hadoop需要落到磁盤、而spark不需要所導致的。但對於小型數據集而言我們則不需要擔心,因為內存足夠不會涉及到分頁,而且numpy、pandas、scikit-learn等工具包也最適合這種小型數據集。因此小型數據集的話,沒有什么可說的,直接處理就可以了。

中型數據集的特點是可以存在本地的磁盤中,但是無法加載至內存,其大小大概是10GB到2TB。當然如果你的機器配置好,加幾個內存條完全可以達到32G,可以輕松讀取20GB的中型數據集,但這與機器的配置有關,這里我們不討論機器性能如何。所以盡管我們可以使用分析小型數據集的工具來分析中型數據集,但是這會造成嚴重的性能損失,因為為了避免內存溢出會不可避免地涉及到分頁。而且這些數據集過大,即便能讀取到內存中,計算起來也是很耗費時間的,因此需要引入並行計算。相比於將所有執行都限制在單核CPU上,並行計算會將工作划分到所有可用的CPU核心,從而大大地提升計算速度。然而在Python中進程之間的通信並不是那么的容易,因此很難在pandas中使用並行計算。

大型數據集的特點是它既不能讀到內存中,也不能存儲在單台機器的硬盤中,這些數據集的大小通過超過2TB,並且根據問題的規模不同,甚至可以達到PB級別、甚至更高。而Python的那些第三方開源工具包:pandas、numpy、scikit-learn則完全不適合這種數據集,因為它們天生就不適合操作分布式數據集。

當然就如我們剛才所說的,這些閾值是比較模糊的,它還取決於你當前的機器配置。就拿筆者來說,目前項目組在做數據遷移時所使用的服務器就是配置比較高的那種,運行內存有256GB,硬盤是幾十個T。但無論如何,當你的數據集在挑戰我們對小型數據集的定義時,尋找可替代的分析工具是有好處的(通常也是必須的)。像pandas,如果內存不夠,那么要么換一台內存容量更大的機器,要么通過分塊讀取來曲線救國,但無論是哪一種情況,顯然都是不合適的。因為通過增加機器內存的方式所需要的成本太高,分塊讀取的話要求你的數據集保證整體處理和分塊處理的效果一致。所以最好的辦法就是換一個工具,但是這也並不是一件容易的事情,因為這通常會導致數據科學家陷入新的困境,比如:使用自己不一定熟悉的技術或者語言,這通常會拖慢他們正在進行的項目的進度。

而Dask的出現就是為了幫助Python程序猿解決這個問題(你說了這么多,終於進入主題了),它於2014年底推出,其目標就是為Python數據科學技術棧帶來原生的可伸縮性,並克服單機限制。隨着時間的推移,這個項目已經發展成為Python開發人員可以使用的最好的可伸縮計算框架之一。Dask由幾個不同的組件和API組成,它們可以分為三層:調度器、底層API、高層API,框架示意圖如下:

我們來分別介紹這些組件,首先Dask的強大之處就在於這些組件和層是具有遞進關系的,其核心便是任務調度器(scheduler),它負責協調和監視跨CPU核心和機器的計算執行。而這些計算在代碼中則是以Dask Dealyed對象和Dask Future對象的形式存在,這兩者區別就是前者是延遲計算(Lazy),這意味着它們只有在需要值的時候計算;而后者則是實時計算,這意味着無論是否需要該值,都是實時計算的;Dask Dealyed對象和Dask Future對象處於Low-level APIs。而Dask的High-level APIs則是在Delayed對象和Future對象的基礎上提供了一個抽象層,里面有Array、Bag、DataFrame、ML,當然它們都是並行的。對這些高級對象操作,會產生許多由任務調度器管理的並行的低級操作,從而為用戶提供更好的體驗,因為不用和低級API打交道。這個設計,為Dask帶來了四個關鍵優勢:

  • Dask是由純Python實現的, 天生可以對numpy、pandas、scikit-learn進行擴展
  • Dask可以很高效的在一台機器上處理中型數據集, 以及在集群上處理大型數據集
  • Dask可以作為一個通用框架, 來對大部分Python對象實現並行操作
  • Dask的配置和維護開銷非常低

Dask能夠在競爭中脫穎而出的第一件事就是它完全使用Python實現,其API集合可以對numpy、pandas、scikit-learn進行擴展,但這並不意味着Dask僅僅只是numpy、pandas的一個鏡像,Dask底層使用的對象依舊來自於numpy、pandas這些庫。一個Dask DataFrame由於許多較小的pandas DataFrame組成,Dask Array由許多numpy Array組成,以此類推。每一個小的底層對象(稱之為"塊"或者"分區")可以在集群中從一台機器傳輸到另一台機器,或者通過隊列的方式在本地進行批次處理。稍后我們將更深入的探討這一過程,總之將中、大型數據集分解為更小的部分,再加上對函數的並行執行的管理,使得Dask可以很優雅地處理那些過於龐大而常規手段無法解決的數據集。使用這些高級對象來支持Dask分布式計算的結果就是,pandas和numpy中的許多函數、屬性,在語法上和Dask是等價的。對於熟悉這些開源第三方庫的Python程序猿而言,這種設計會使得他們在處理小型數據集到處理中、大型數據集的轉換變得非常容易。與學習新的語法相比,數據科學家可以正價專注於可伸縮計算的最重要的方面:編寫健壯、高性能、以及針對並行計算進行優化的代碼。

其次,Dask在單機上處理中型數據集和在集群上處理大型數據集一樣有用,因為Dask無論是向上擴展還是向下擴展都非常靈活,這使得用戶可以很容地在本地機器上創建任務原型,並在需要時可以無縫地將這些任務交給集群。無需對現有代碼進行重構,也無需為集群的特定問題(如:資源管理、恢復、數據移動)而單獨編寫額外的代碼,直接就可以完成單機向集群的轉換。而且Dask還為用戶提供了很大的靈活性,支持選擇部署和運行代碼的最佳方式。通常使用集群來處理中型數據集是完全沒有必要的,相反由於協調多台機器一起工作所花費的開銷有時還會讓程序變得更慢。Dask在內存占用上做的非常多的優化,因此它可以在單機上優雅地處理中型數據集,即使在性能相對較低的機器上。這種透明的可伸縮性要歸功於Dask的內置任務調度器(scheduler),當Dask在一台機器上運行時,可以使用本地調度器,而分布式任務調度器可以用於本地執行和跨集群執行。Dask還支持與流行的集群資源管理器(如:yarn、mesos、k8s)進行交互,允許你使用帶有分布式任務調度器的現有集群。無論是跨多少機器,配置任務調度器和使用資源管理器部署Dask都只需要很少的工作。而在整個系列中,我們將討論不同配置下運行Dask,比如:使用本地任務調度器在本地運行,以及使用帶有docker和亞馬遜彈性容器服務(這里筆者使用的是阿里雲)的分布式任務調度器在雲端的集群中運行。

Dask最不尋常的方面之一就是它具有可伸縮大部分Python對象的能力,Dask的低級API,如:Dask Delayed和Dask Future對象,它們是對Dask Array中使用的numpy Array,Dask DataFrame中使用的pandas DataFrame,Dask Bag中使用的Python list進行擴展的共同基礎。

最后,Dask非常輕便,易於安裝、卸載和維護,它的所有依賴都可以使用pip或者conda來安裝。而使用docker構建和部署集群的鏡像也非常容易,我們在后續將會做這些工作,而且Dask只需要很少的配置就可以開箱即用。由於這個原因,Dask不僅能夠很好的處理重復出現的作業,而且還是構建概念證明和執行特定數據分析的優秀工具。

卧槽,感覺你都快把Dask說上天了,后面我要是發現不好用,我就敢把博客全刪了。

初識Dask的所有數據科學家腦海里應該都有一個問題,那就是Dask和其它表面上類似的技術(比如:Apache Spark)相比有什么不同。首先Spark已經成為分析大型數據集的一個非常流行好用的框架,並在這方面做得很好。然而,盡管Spark支持包括Python在內的多種語言,但它的開發語言是基於jvm之上的Scala,這或許會給非java程序猿帶來一些專業知識上的挑戰。Spark於2010年推出,作為Apache Hadoop的處理引擎MapReduce的替代品,其核心嚴重依賴於java虛擬機(jvm)。在幾個發布周期之后,出現了PySpark,正式宣布Python和Spark的結合。但是PySpark僅僅支持Python和Spark集群進行交互,提交給Spark的任何Python代碼都必須使用Py4J庫來jvm進行交互,這使得調優和調試PySpark代碼非常困難,因為有些執行發生在Python上下文之外,而且Spark的一些數據結構還不支持Python。

所以PySpark的用戶最終可能會決定將自己的代碼采用Scala或java進行重構,以最大限度地利用Spark。Spark的新特性和性能上的提升都優先被添加到java和Scala api中,通常需要經過幾個發布周期之后才能將該功能公開給Python,或者說PySpark。此外,PySpark的學習曲線並不平緩,它的DataFrame api雖然在概念上類似於pandas中的DataFrame,但在語法和結構上有很大的差異。這意味着PySpark的新用戶必須重新學習如何遵循"Spark way"的方式做事,而不是基於現有的經驗、知識和pandas、scikit-learn一起工作。Spark經過了高度的優化,可以對集合對象進行計算,比如對一個數組進行map改變里面的每一個元素,以及計算數組的和。但是這種優化是以靈活性為代價的,如果不能表示為對集合的映射、或者reduce類型的操作,那么Spark是無法計算的,所以Spark無法像Dask那樣優雅地擴展自定義算法。而且Spark也因其配置繁瑣而臭名昭著,需要很多依賴項,比如:Apache Zookeeper和Apache Ambari,而且這些依賴項自己也很難配置。所以對於重度依賴Spark和Hadoop的公司或組織,一般來說都會擁有一個專門的團隊,主要職責就是配置環境、監控和維護集群。

上面的比較並不是為了黑Spark,Spark是一個非常優秀的框架,擅於做它所做的事情,當然是分析和處理大型數據集的可行方案。然而Dask的短學習曲線、靈活性和熟悉的api,使得它在使用Python做數據分析的數據科學家眼中是一個更具有吸引力的解決方案。

我希望你現在明白為什么Dask是一個如此強大和靈活的工具了,而且如果我之前的懷疑是正確的,那么你選擇這本書的目的是因為你正在和一個大型數據集做斗爭(不,我只是單純的學習Dask以備后用罷了)。我希望你嘗試使用Dask,學習更多使用Dask處理大型真實數據集的知識,並為之感到興奮。但是在研究Dask代碼之前,最好先回顧一下幾個核心概念,它們將幫助你理解Dask的任務調度程序是如何"分而治之"計算的。如果你不熟悉分布式計算的概念,那你一定要看下去,因為掌握任務調度機制將會使你更好地理解在執行計算時究竟發生了什么,以及潛在的瓶頸可能在哪里。

什么是DAGs

Dask的任務調度器使用"有向無環圖( directed acyclic graph,簡稱DAG)"來對計算進行組織、控制和表示。DAGs(多個DAG,單詞的復數形式)來自於一個被稱為圖論的更大的數學體系,這里圖論和你想象的餅圖、柱狀圖沒有任何關系。相反,圖論是將一組具有關聯的對象使用一張圖來表達出來,雖然這個定義非常的抽象,但它意味着圖非常善於表達各種各樣的信息。我們通過生活中的例子,來更好的理解DAG。

比如我要和蕾姆吃晚飯:

當我們想要完成一個功能的時候,需要執行很多的函數,當然在任務調度框架中我們執行的是task(任務),負責執行task的是worker,比較熟悉的概念了。另外task也是基於某個函數創建的,當執行task的時候,實際上執行的就是里面的函數,只不過task內部除了要執行的函數(步驟)之外,還包含了其它的額外信息,比如task的創建時間、內部函數的執行狀態等等,這些都要進行動態更新。

而每一個task都對應圖中的一個節點或者步驟,而且這些節點會遵循一定的邏輯順序,比如:吃飯之前得先回家、然后洗手,這意味着在下一個task執行之前,必須先將前面的一個或多個task執行完畢才可以。

正如圖中顯示的那樣,如果我要和蕾姆吃飯,那么蕾姆必須要先煮飯和炒菜,以及我必須要先回家。至於蕾姆煮飯、蕾姆炒菜、我下班回家這三者本身則沒有任何聯系。這三個步驟的完成順序並不重要,但是必須要完成所有的步驟之后才能進行最后的吃飯過程。

另外你可能還發現了,那就是圖中的箭頭是單向的,后面的節點沒有指向前面的節點。因為一個節點一旦完成,就不會重復或者重新執行,所以該圖才叫有向無環圖。有向指的是箭頭代表着方向,無環指的是箭頭是單向的,如果是雙向則會形成一個環。

而且在"下班回家"之后,如果想吃飯還必須要先洗手換衣,像這種想要完成一個節點之前還需要間接滿足的依賴叫做傳遞依賴。

擴展、並發和恢復

我們知道當內存不夠的時候,可以采用多台機器的方式,但是要怎么擴展呢?多個worker之間如何協調,才能更好地協同工作呢?並且執行失敗之后如何進行錯誤處理和故障恢復呢?

橫向擴展與縱向擴展

假設有一天蕾姆突然說,自己想要當廚師,想讓更多的人來品嘗自己的手藝。所以她便開了一家店,結果包括老八在內,吃過的人都說好,就這樣越來越多的人來店里吃飯。但這樣就出現了一個問題,蕾姆不得不在晚餐高峰時期為一大群飢餓的顧客做飯,如果這樣的話,花費的時間會大幅增加。就拿切菜來說吧,以前只需要切兩人份,現在要切幾十人份,花費的時間大幅度提高,更不要提炒菜所花費的時間了。而解決這個問題有兩個辦法,一是使用切菜和炒菜更快、更有效的設備來替換現有的設備(縱向擴展);二是雇佣更多的工人(worker),大家一塊並行工作(橫向擴展)。

決定是橫向擴展還是縱向擴展並不是一件容易的事情,因為兩者都有利弊。如果是縱向擴展,那么蕾姆仍然要從切菜到炒菜,一個人執行完整個過程,但是她不需要擔心溝通的問題,也不用擔心別人做的沒有自己做的好吃。因此面臨的是中型數據集的話,那么縱向擴展是一個正確的選擇。

但是隨着名氣越來越大,來的人也越來越多,即便切菜用的刀再鋒利,炒菜用的設備再好,也終有撐不住的那一天。這時候我建議蕾姆多找幾個人一起工作,但是人如果多了,就意味着要買額外的刀、砧板以及其它額外工具,並且還需要提供足夠的設施,並且還要能夠為員工發工資。所以從長遠的角度來看,這是一個更具有成本效益的解決方案,它需要投入大量的資金。而且工人還有可能會生病從而耽誤工作,或者做一些意想不到的事情讓人煩惱。如果廚房里面只有三到四個廚師,那么還可以盯着他們,但是隨着廚房規模的擴大,那么可能就需要一個副廚師長了。同樣的,這些都和成本有關,在考慮是橫向擴展還是縱向擴展時,應該如實考慮這些成本。

但最終蕾姆還是選擇了橫向擴展,組建了相應的廚師團隊,那么現在她必須弄清楚如何向每位廚師傳達指示,並且確保食譜按時出爐。所以這個時候便可以使用有向無環圖,這是一個非常偉大的工具,它可以用於規划和協調復雜的任務(即使不在一個工作池中)。最重要的是,節點之間的依賴關系有助於確保工作能按照一定順序有條不紊的進行(注意:一個節點只有當所有指向它的節點全部完成之后,才會開始執行)。另外,一個節點是一個獨立的工作單元,因此可以讓多個廚師做相同的事情,比如:指定三個廚師都負責切菜,兩個廚師都負責做點心等等,而對廚師進行任務划分和監督的副廚師長,便扮演着Dask任務調度器的角色。並且為了讓食物更高效的在房間里面流動,副廚師長應該不斷地評估每個廚師需要做什么工作,在什么時候做。比如:蒜薹炒肉,當廚師A准備切蒜薹時,副廚師長應該讓廚師B准備切肉,而不是等廚師A將蒜薹都切完了再下達指令。這種策略可以讓一些顧客能更快地得到服務,同樣的Dask任務調度器也在多個任務之間循環工作,從而減少內存負載和更快地發出任務。任務調度器會以一種非常有效的方式將工作單元分配給機器,並且會使工作池的空閑時間達到最小,組織多個worker之間並行執行,並為每個任務分配一定數量的worker。

並發和資源管理

通常情況下,除了數量你還需要考慮更多其它的限制因素,在可伸縮計算中這些被稱為並發問題。比如蕾姆的廚房里面只有五把刀,這意味着同時只能有五個廚師切菜。如果已經有五個廚師在切白菜了,但是還有一個廚師需要切洋蔥,然而已經沒有刀可以供他使用了,那么它此時就要處於等待狀態了,直到某一個廚師將白菜切完,或者給他分配一個不需要使用刀的任務。假設切白菜需要的時間很長,那么切洋蔥的廚師就需要等待很長的時間,這種資源分配顯然是不合理的。

並且當共享資源被使用時,會有一個資源鎖,保證鎖定該資源的工作人員在執行任務的途中不會被其它工作人員"搶奪"。如果一個廚師從另一個廚師手中奪取資源(刀)的話,那么這是非常危險的,而且也會花費無意義的時間。因此副廚師長必須指定基本規則來化解這些沖突,這些規則包括誰可以使用相應資源,以及當資源可用時能夠用它來做什么。所以這也是任務調度器要做的事情,可伸縮框架中的任務調度器必須能夠決定如何處理資源的分配和鎖定,但如果處理不當,資源爭奪可能會對性能造成很大的不利影響。但幸運的是,大多數框架(包括Dask)都非常擅於高效的任務調度,通常不需要手工調優。

故障恢復

最后,還有一個故障恢復策略,如果沒有這個,那么可伸縮計算框架就不完整。就將副廚師長無法同時監督所有廚師一樣,隨着集群中機器數量的增加,對任務分配的協調處理也會變得越來越困難。由於最終結果是由所有單獨的操作聚合得到,因此要確保所有部分都是准確的,但機器和人一樣也會犯錯誤,所以必須考慮兩種典型的故障:節點故障和數據丟失。舉個栗子:如果讓廚師連續切三個小時的白菜,它肯定會瘋的,再也無法忍受這種單調,從而脫下外套走到門外思考人生。這個時候,便出現了節點故障,因為該節點不工作了。因此副廚師長就需要找到另一個廚師來頂替剛才那個廚師的位置,因為白菜必須要有人切,但是新來的廚師可以從上一個廚師中斷的地方開始切,所以這還是一個沒有數據丟失的節點故障。不需要對數據重新計算,因此對性能的影響也就沒那么嚴重。

但是當發生數據丟失時,對性能造成的影響就沒那么小了。假設此時一道菜已經完成了一半,但是某個廚師腳老眼一花不小心把鹽當成了糖撒到菜里面了,這個時候顯然不能繼續下去了,廚師不得不從頭開始。因為節點之間的依賴關系不能滿足,必須一步步回退到第一個無依賴的節點,然后從那里重新開始。盡管這是一個非常糟糕的示例,但重要的是,我們需要記住:有向無環圖中的任何一個節點發生故障,都可以回退到某個無依賴的節點上從頭來過,區別就是回退所邁的步伐多與少罷了。如果倒霉的話,就相當於從頭開始了。而任務調度器則負責停止部分工作,並從執行失敗的worker那里重新分發相應任務,確定任務量,指定新的worker來執行。

在極少的情況下,任務調度器也可能會遇到問題而失敗,就像蕾姆的副廚師長決定掛起帽子、走出房門一樣。這種失敗當然也是可以恢復的,但是由於只有任務調度程序知道完整的DAG和以及任務完成了多少,所以唯一的選擇是用一個全新的任務圖從第一步重新開始。但說實話,廚房的類比有點站不住腳,因為事實上廚師們會很了解食譜,自己知道該做什么,不可能每一步都需要副廚師長。但是Dask的情況不是這樣,worker只是按照要求去執行,如果沒有任務調度器告訴它該做什么,它們是無法自己做決定的。

希望你現在已經很好的了解DAG的強大功能,以及它們與可伸縮框架之間的聯系,這些概念肯定會再次出現,因為Dask的任務調度都是基於這里介紹的DAG概念。下面我們來介紹一下后續使用的數據集,以及Dask的操作和功能。

數據集

因為我們要使用Dask進行實際演練,所以我們必須要有相應的數據集,並且這些數據集最好不是那種手動簡單生成的"玩具數據集",將我們的實際操作應用在更真實、更混亂的數據集中會更有價值。因為使用適當大的數據集可以獲得更多的經驗,因為這樣未來在面臨未知的大型數據集時就會有更多的經驗去應對,因此在后續我們將會使用NYC OpenData(https:// opendata.cityofnewyork.us)提供的強大的公共域數據集來學習Dask。

每個月的第三周,紐約市財政部都會記錄並發布一組數據,其中包括本財年迄今為止的所有停車罰單,這個城市收集的數據非常豐富,甚至包括一些有趣的地理特征。並且為了讓數據更容易獲取,有兩個人收集紐約市OpenData四年來的數據,並將其發布在了熱門的機器學習網站kaggle上。數據集的跨度從2013到2017年6月,未壓縮的數據集超過8GB。如果你有一台強大的計算機,那么這個數據集對於你來說可能滿足小型數據集的定義,但是對於大多數人來說,它應該是一個大小適中的數據集,數據集在www.kaggle.com/new-york-city/nyc-parking-tickets上面獲取,當然我們還可以進入www.kaggle.com/datasets頁面下載其它感興趣的數據集。

總結

  • Dask可以擴展流行的Python數據分析庫,比如pandas和numpy,允許你輕松地分析中大型數據集。
  • Dask使用有向無環圖(DAG)來協調CPU內核和機器之間並行代碼的執行。
  • 有向無環圖由多個節點組成,明確定義了開始和結束,單個遍歷路徑以及沒有循環。
  • 一個如果節點想要執行,那么必須先保證指向它的節點都執行完畢。
  • 橫向擴展(說白了就是加機器)通常可以提高性能,但它會產生額外的開銷,因為通信也是需要耗費資源的。
  • 在出現故障時,可以針對故障節點重新執行,不會影響其它節點。

這一章全尼瑪是字,翻的我都快吐了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM