天天看點

《Pig程式設計指南》一1.1 Pig是什麼?

本節書摘來異步社群《pig程式設計指南》一書中的第1章,第1.1節,作者: 【美】alan gates 譯者: 曹坤,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

pig程式設計指南

pig提供了一個基于hadoop的并行地執行資料流處理的引擎。它包含了一種腳本語言,稱為pig latin,用來描述這些資料流。pig latin本身提供了許多傳統的資料操作(如join、sort、filter等),同時允許使用者自己開發一些自定義函數用來讀取、處理和寫資料。

pig是一個apache開源項目。這意味着使用者可以免費下載下傳源碼或者二進制包,自由使用它,對這個項目貢獻自己的代碼,同時也可以在apache license的許可範圍下将pig用到自己的産品中或者在需要的時候修改代碼來滿足特定需求。

pig運作于hadoop之上,它同時使用到hadoop分布式檔案系統hdfs和hadoop處理系統mapreduce。

hdfs是一個分布式檔案系統,它将檔案存儲到hadoop叢集的各個節點上。它負責将檔案分割成許多資料塊然後分發到不同的節點機器上,其中包括對每個資料塊進行多份備援備份,這樣可以避免因為某台機器宕掉而造成資料丢失。hdfs提供了一種類似posix的使用者互動形式給使用者。預設情況下,pig從hdfs中讀取輸入檔案,使用hdfs來存放mapreduce任務所生成的中間資料,最終将輸出寫入hdfs中。在第11章,使用者将看到pig不隻是可以從hdfs中讀取輸入檔案或将輸出檔案寫入hdfs的。

mapreduce是一個簡單而強大的并行資料處理算法。mapreduce計算架構下的每個任務都由3個主要階段組成:map階段、shuffle階段和reduce階段。在map階段,程式可以并行獨立操作輸入資料中的每一條記錄。因為可以同時運作多個map任務,是以即使輸入的資料量達到gb或者tb級别,隻要有足夠多的機器,map階段通常在1分鐘内就可以完成。

mapreduce任務的一個特别之處在于需要确定資料是根據哪個鍵進行收集的。例如,假設使用者在處理一個網站的web伺服器日志,而且這個網站需要使用者登入後才能操作,那麼使用者就可能會使用使用者id作為資料收集的鍵,因為通過這個使用者id就可以知道每個使用者在這個網站上的對應的所有操作。map階段後緊跟着就是shuffle階段,在這個階段資料已經根據使用者指定的鍵收集起來并且分發到不同的機器上去了,這是為reduce階段做準備。包含同一鍵的所有記錄将會交由同一個reducer處理。

在reduce階段,程式将提取每個鍵以及包含該鍵的所有記錄。這個過程也是在多台機器上并行執行完成的。當處理完所有組時,reducer就可以寫輸出了。下面我們将通過一個簡單的mapreduce程式進行示範。想更多地了解mapreduce是如何工作的,請看附錄b“mapreduce介紹”。

假設現在有一個mapreduce程式對一個文本檔案進行詞頻統計。該程式本身是mapreduce提供的示範程式。在這個例子中,map階段會從文本檔案中一次讀取一行,然後分割出每個詞作為一個字元串,之後對于分割出的每個單詞,會輸出單詞本身以及數字1,數字1表示這個單詞出現過1次。在shuffle階段,将使用單詞作為鍵,哈希分發對應的記錄到不同的reducer中去。在reduce 階段會将相同的單詞對應的出現次數相加,并最終将求和後的數值和單詞本身一起輸出。以童謠“mary had a little lamb”為例,輸入将是:

這裡假設每一行都被發送到不同的map任務中去了。當然事實上,每個map任務處理的資料要遠遠大于這個數量,這裡隻是為了後面更好地去描述。mapreduce整個過程的資料流如圖1-1所示。

map階段一旦結束,shuffle階段将會把包含相同單詞的所有記錄送出到同一個reducer中。對于這個例子我們假設有兩個reducer:以a~l開頭的單詞送出到第一個reducer中,而以m~z開頭的單詞送出到第二個reducer中。這兩個reducer最終将會把每個單詞的出現次數分别相加然後輸出。

pig的所有資料處理過程都是使用mapreduce來執行的。pig将使用者所寫的pig latin腳本編譯成一個或者多個mapreduce任務,然後在hadoop上執行。例子1-1展示了如何使用pig latin腳本來對童謠“mary had a little lamb”進行詞頻統計。

《Pig程式設計指南》一1.1 Pig是什麼?

例1-1 使用pig對童謠“mary和她的羔羊”進行詞頻統計

在使用pig時無須去過度關注map、shuffle和reduce階段,因為pig會将腳本中的操作解析成相應的mapreduce階段。

pig latin是一種資料流語言,這意味着它允許使用者去描述如何從一個或多個資料源并行讀取資料,然後并行地進行處理,最後将處理結果并行地輸出到一個或多個結果集中。這些資料流可以像前面提到的那個詞頻統計例子一樣是個簡單的線性流。同時它們也可以是複雜的工作流,其中可以包含一些加入多個輸入的節點,也可以包含一些将輸入資料分割成多個流的節點,這些節點都是通過不同的操作符來處理的。用數學語言來描述的話,pig latin 描述的是一個有向無環圖(dag),在這個圖中,節點代表處理資料的操作符,節點間的向量代表資料流。

這意味着pig latin和使用者之前見過的許多程式設計語言會有所不同。在pig latin中沒有if語句,也沒有for循環操作。這是因為傳統的過程語言和面向對象語言描述的是控制流,而資料流隻處于一個從屬地位。而pig latin更專注于資料流。想了解pig latin腳本中如何在處理資料流的同時加入控制流,請閱讀第9章。

查詢語言和資料流語言的比較

大體一瞥,人們會說pig latin不過是sql的一個面向過程化的版本。盡管确實有一定的相似性,但是其實兩者具有非常多的差異。sql是一種查詢語言,它關注于允許使用者構造查詢,它允許使用者去描述他們想得到什麼問題的答案,而不是如何給出問題的答案。然而在pig latin中,使用者可以較長的描述如何對輸入的資料進行處理。

pig latin和sql的另一個主要差別是sql面向的是回答一個問題,是以當使用者想同時進行多個資料操作時,他們要麼使用多個查詢語句,這時需要将一些查詢的中間資料存放到臨時表中;要麼寫一個大的包含子查詢的查詢語句,将一些初始的處理過程由子查詢來完成。然而,很多使用者發現子查詢令人困惑而且也并非那麼容易去建構。同時,子查詢使用的是由内而外的設計,也就是說,在資料管道最裡面的子查詢會最先執行。

pig被設計為實作知道将要進行的一系列的資料操作,是以不需要通過颠倒順序的子查詢的方式來寫資料管道,也無需使用臨時表來存放中間資料。這點将通過例子1-2和例子1-3來進行示範。

現在假設有個使用者想先按某個鍵對表進行group分組操作,然後和第二張表進行join連接配接操作。在sql查詢中,因為join操作發生在group操作之後,是以要麼使用子查詢,要麼寫兩個查詢語句,同時将中間結果儲存到臨時表中。例子1-3用到了一個臨時表,因為這樣可讀性要好些。

例1-2 sql中先進行分組然後進行連接配接操作

在pig latin中,是另外一種方式,如例1-3所示。

例1-3 pig latin中先進行分組然後進行連接配接操作

此外,sql和pig latin各因不同的應用場景而生。sql的應用場景是rdbms,在這種場景下,資料是标準化的,并且加上了模式和其他一些特有的限制(例如,null值也是不可以脫離限制單獨存在的等)。pig是為hadoop資料處理環境而設計的,在這種環境下,模式有時是未知的或不一緻的,資料可能沒有進行恰當的限制而且很少進行資料标準化。基于這些不同,pig不需要将資料事先導入表中,當資料導入hdfs中後,它就可以直接操作這些存放在hdfs的資料。

如果語言和文化類似,那麼融入一個新的環境可能會更加容易些。我和妻子一起去過法國幾次。我會講很少的法語,但是因為是商業語言(或許是因為美國人和大不列颠人喜歡到法國度假),對于我來說法語中已經包含了足夠多的英語口語,使我足夠應付得了。而我的妻子,她會講法語。她在法國有朋友去拜訪時,她可以和他們很好地交談。她可以去那些不在通常的旅遊線路上的其他景區探險。她的法國經曆比我要多得多,因為她會講當地本土語言——法語。

在資料處理範疇裡,sql就是英語。它有個非常好的特點就是無論是人還是工具都認識它,也就是說它的入門門檻很低。我們的目标是使pig成為像hadoop這樣的并行資料處理系統範疇裡的母語。盡管這可能要求使用者需要進行一定的學習才能使用,但是它可以讓使用者更加充分地利用hadoop提供的計算能力。

pig和mapreduce的差別是什麼

我剛剛聲明pig團隊的一個目标是使pig latin成為像hadoop這樣的并行資料處理環境的母語。但是難道mapreduce提供的還不夠嗎?有必要使用pig嗎?

pig比直接使用mapreduce有幾個優點。pig latin 提供了所有标準的資料處理操作,例如join、filter、group by、order by、union等。mapreduce直接提供了group by操作(也就是shuffle和reduce兩個階段做的事情),同時通過實作分組操作間接地提供了order by 操作。過濾器操作和推測執行操作可以在map階段進行簡單實作。但是其他的操作,特别是join操作無法提供,是以必須由使用者自己進行代碼實作。

pig提供了一些對這些标準的資料操作的複雜的、完備的實作。例如,因為每個鍵對應的記錄的個數很少是均勻地分布在叢集中的,是以送出給reducer的資料經常會産生資料傾斜。也就是說,有的reducer需要比别的reducer處理10倍或更多倍的資料。pig具有join和order by 操作可以處理這種情況,而且(在一些情況下)可以重新均衡reducer負荷。這些需要pig團隊花費好幾個月的時間編寫mapreduce程式,然後再重構代碼,這确實耗費時間。

在mapreduce中,在map階段和reduce階段的内部的資料處理對于系統來說是不透明的。這意味着mapreduce沒有機會優化或者檢查使用者的代碼。pig另一方面,可以通過分析pig latin腳本來了解使用者描述的資料流。這意味着pig可以在早期進行錯誤檢查(例如使用者是否将一個string類型的字段放到一個integer類型的字段中?)和進行優化(例如這兩個group操作是否可以合并?)

mapreduce沒有一個類型系統,這是有意這麼設計的,因為這樣可以給使用者更大的自由度去使用他們自己的資料類型和序列化架構。但這樣就産生了一個不好的問題,就是限制了系統在運作前和運作時對使用者代碼進行檢查的能力。

這幾個方面都表明pig latin相對于mapreduce java代碼更容易編寫和維護。我做了一個并非科學的實驗,對于同一個操作我分别使用pig latin和mapreduce進行實作。假設有個檔案存有使用者資料,另一檔案存放了對于某個網站的點選資料,例子1-4所示的pig latin腳本将找到年齡為18~25歲的使用者通路最多的5個頁面。

例1-4 查找通路次數最多的前5個url

這段腳本的第1行表示加載檔案名為users的檔案,同時聲明這份資料有兩個字段:name和age,而且為這個輸入取别名為users。第2行是個過濾器,将users中age這個字段值大于等于18而且小于等于25的記錄過濾出來,不滿足條件的資料将被忽略。經過過濾器後,留下的資料就是在我們感興趣的年齡範圍内的了。我們将這個過濾器的結果取别名為fltrd。

第3行是第2個load加載資料語句,這個語句加載了檔案pages,并取别名為pages,它聲明了兩個字段:user和url。

“jnd = join”這一行以fltrd.name和pages.user為鍵,對fltrd和pages進行join連接配接操作。通過這次join操作我們就可以得到每個使用者通路過的所有url連結了。

“grpd = group”這一行按照url進行分組。結果是每一個url,例如pignews.com/frontpage,都對應着一組url字段中包含了對應值的所有記錄。緊跟着的下一行會統計每個url對應的記錄個數。這一行後我們就知道了,對于每個url,被年齡為18~25歲的使用者通路了多少次。

之後的一件事就是按通路次數從通路最多到通路最少進行排序。“srtd = order”這一行就是根據前一行的統計結果進行desc(降序)排列。是以,最大值将在第1行。因為最終我們隻需要最前面的5條記錄,是以最後一行将統計結果限制在前5行。最後的結果重新存放到hdfs中一個叫做top5sites的檔案中。

在pig latin中整個處理過程需要寫9行代碼,耗時在15分鐘左右,其中包括寫代碼和對代碼進行調試的時間。如果以mapreduce(這裡省略了)來寫的話,需要差不多170行的代碼而且花費了我4個小時的時間才調試成功。pig latin同樣利于維護,因為這段代碼,對于後來的其他開發者同樣是容易了解和友善修改的。

當然pig所帶來的這些便利同樣是有代價的。通過mapreduce架構可以開發一些算法,在pig中卻很難實作。同時對于開發者,他們需要放棄一個層次的控制權。一名優秀的工程師,隻有給予足夠的時間,總是可以将一個普通的系統做得足夠好。是以對于不常見的算法或者是對于性能要求很高的話,這種情況下使用mapreduce仍然是正确的選擇。基本上這種情況也和選擇java編碼而不選擇使用像python這樣的腳本語言的情形是一樣的。java功能強大,但是因為它是進階程式語言,是以使用它開發需要比腳本語言花費更多的時間。開發者需要根據實際情況選擇合适的工具。

以我的經驗,pig latin的使用場景可以分為獨立的三大類:傳統的抽取轉換加載(etl)資料流、原生資料研究和疊代處理。

最大的使用場景就是資料流了。一個通常的例子就是網絡公司從他們的web伺服器上收集到日志,進行資料清洗,之後進行簡單的聚合預計算,然後導入資料倉庫中。在這種情況下,資料被加載到計算網格中,之後使用pig從資料泥潭中清理出有價值的資料。同時還可以使用pig将使用者網頁操作資料和使用者資料庫資訊進行join連接配接,這樣可以将使用者cookie和已知的使用者資訊關聯起來。

另外一個資料流應用的例子是使用pig處理離線資料來建立使用者行為預測模型。pig被用來掃描所有的使用者和網站的互動資料,最終将使用者分為各種各樣的群組。然後,對于每個群組會生成一個數學模型,根據該模型可以預知這個群組的使用者對各種類型的廣告或者新聞文章的反映是什麼樣子的。通過這種方式,網站可以知道展示什麼樣的廣告可以更有可能獲得更多的點選,或者釋出什麼樣的新聞故事可以更有可能吸引使用者和挽留使用者再次通路。

傳統上,使用像sql這樣的語言執行點對點的查詢可以快速地為問題準備好相應的資料。然而,對于原始資料的研究,一些使用者還是偏向使用pig latin腳本。因為pig可以在無模式,模式資訊不全,或者模式不一緻的情況下進行操作,同時因為pig可以很容易地控制封裝的資料,是以對于那些期望在資料沒有進行清洗也沒有寫入資料倉庫的情況下,分析資料的研究人員經常更偏好于使用pig。經常處理大規模資料集的研究人員經常會使用像perl或者python這樣的腳本語言進行處理。具有這些使用背景的使用者通常更喜歡使用pig這樣的資料流範式而非像sql那樣的聲明式查詢語言。

建立疊代處理模型的使用者也開始使用pig。假設有一個新聞門戶網站,它保留了一個它跟蹤的關于該網站的所有新聞故事的圖。在這個圖中每個新聞故事都是一個節點,節點間的連線表示的是相關故事間的關系。例如,所有關于即将來臨的選舉的故事都是聯系到一起的。每5分鐘都有一組新的故事進來,這時資料處理引擎需要将這組故事增加到圖中。這些故事中有一些是新的,有一些是對之前的故事進行的更新,還有一些是替代之前已經存儲的一些故事的。這時需要對整個故事圖做一些資料處理步驟。例如,對于建立行為目的模型的處理過程就需要将使用者資料和整個故事圖進行連接配接。每5分鐘重新運作整個圖是不可行的,因為對于适當數量的硬體資源來說在5分鐘内運作出結果是不可能的。但是模型建立者不想隻是每天更新一次這些模型,因為那意味着會錯過一整天的時間來提供機會。

為了應付這個問題,有必要定期地首先對整個圖進行連接配接,例如可以按照天來進行連接配接。然後,每5分鐘一旦有資料進來,就可以立即完成對新進來的資料進行連接配接操作,同時這個結果是可以和對整個圖做連接配接的結果整合在一起的。這個組合步驟并不容易,因為需要在5分鐘内完成對整個圖進行插入、更新和删除操作。使用pig latin來表達這種組合關系是可以的并且是相當友善的。

目前所說的一切都隐含着一點:pig(與mapreduce一樣)是面向資料批處理的。如果需要處理的是gb或者tb數量級的資料,那麼pig是個不錯的選擇。但是因為它期望的是序列地讀取一個檔案中的所有記錄然後序列地将輸出寫入存儲中,是以對于那些需要寫單條或者少量記錄,或者查詢随機序列下的多條不同記錄這樣的任務,pig(與mapreduce一樣)并非是個好選擇。關于在這些情況下選用什麼樣的軟體才是合理的更多讨論請檢視第12.3節“nosql資料庫”。

在早期,作為潛在貢獻者加入pig項目的人們并非了解這個項目究竟是關于什麼的。他們并不清楚怎樣做才是最好的貢獻或者哪些貢獻會被接受以及哪些不會被接受。是以,pig團隊釋出了一個項目設計思想聲明,其内容總結為pig渴望成為:

pig什麼都吃

不管資料是否有中繼資料,pig都可以操作。不管資料是關系型的、嵌套型的,或者是非結構化的,pig也同樣可以操作。而且它還可以很容易地通過擴充,不單單可以操作檔案,還可以操作key/value型的存儲,以及資料庫等。

pig無處不在

pig期望成為一種并行資料處理語言。它不會局限于是一種特殊的并行處理架構。它首先是基于hadoop之上的實作,但是我們期望它并非隻能在hadoop平台上使用。

pig是家畜

pig被設計為可以讓使用者很容易地控制和修改的語言。

pig允許使用者随時整合加入他們的代碼,是以目前它支援使用者自定義字段類型轉換函數、使用者自定義聚合方法函數和使用者定義條件式函數。這些函數可以使用java來寫也可以使用最終可以編譯成java代碼的腳本語言(例如jython)編寫。pig支援使用者定義的加載和存儲函數。pig通過自己的stream 指令和需要mapreduce相關的jar包的mapreduce指令可以執行外部的執行指令。pig同樣允許使用者為自己的特定使用場景提供一個使用者自定義的分區方法函數,使他們執行的任務在reduce階段可以達到一個均衡的負荷。

pig有一個優化器,它可以将pig latin腳本中的操作過程進行重新排列以達到更好的性能,例如将mapreduce任務進行合并等。但是,如果對于某種情形下這種優化是不必要的話,使用者可以很容易地将最優控制器關閉,這樣執行過程就不會發生改變。

pig會飛

pig處理資料很快。我們會持續地優化性能,同時不會增加一些使pig顯得較重而降低性能的新功能。

繼續閱讀