天天看點

《MapReduce設計模式》一1.4 Hadoop示例:單詞計數

本節書摘來異步社群《mapreduce設計模式》一書中的第1章,第1.4節,作者: 【美】donald miner , adam shook 譯者: 徐钊 , 趙重慶 責編: 楊海玲,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視。

在介紹完mapreduce的整個處理過程之後,讓我們來看一個簡單的示例:單詞計數(word count)。“單詞計數”程式是一個典型的mapreduce示例,因為它既簡單,又很适合使用mapreduce高效地處理。很多人會抱怨說“單詞計數”作為示例已經被用過太多次了,希望本書後面的内容能彌補這一點!

在這個特定的示例中,我們将對stackoverflow網站上使用者送出的評論進行單詞計數。網頁中text域的内容将被抽取出來并做一些預處理,然後我們再計算每個詞出現的次數。這個資料集中的示例記錄如下:

這條記錄是stackoverflow的第8 189 677條評論,貼子數為6 881 722,使用者數是831 878。postid的數量和userid數量作為外鍵可以和資料集中的其他部分資料進行關聯。我們将在本書的第5章介紹如何實作這種關聯。

我們分析的第一塊代碼是驅動程式(driver)部分。驅動程式的作用将mapreduce作業的所有元件組合起來然後送出執行。這些代碼一般都是通用的并且被作為“通用模闆”。在後面介紹的程式設計模式中你會看到,我們大部分的驅動程式都是相同的。

下面這些代碼演變自hadoop core代碼中的“word count”示例。

驅動程式的作用是協調整個任務。main函數中的前幾行代碼都是在解析指令行輸入參數。然後,開始設定job對象的參數,包括計算過程中用到的類以及輸入、輸出路徑。這就是驅動程式的全部!最重要的一點是要確定設定的類名和你編寫的類名是一緻的,并且輸出的鍵、值類型和mapper定義的一緻。

在後面介紹的不同模式中,上述代碼裡唯一會變化的是job.setcombinerclass方法。某些情況下,因為reducer的特性,combiner将不會被用到。在另外一些情況下,combiner類将不同于reducer類。不過在“單詞計數”程式中,使用combiner會非常高效,并且啟用起來非常簡單。

接下來看mapper代碼是如何解析和準備文本的。當标點符号和随機文本被清理掉後,文本字元串将被分割成一個單詞清單。然後,産生的中間鍵是每個單詞,其對應的值為“1”,這表示這個單詞已出現過一次。即使一個單詞在一條記錄中出現了兩次,輸出的依然是鍵=該單詞、值=1,不過會有兩個這樣的鍵/值對,這些鍵/值對将在後面處理。最終,所有這些鍵對應的值彙總求和就能得到每個單詞出現的總次數。

第一個函數mrdputils.transformxmltomap是一個輔助函數,它按照通用的方式逐條解析stackoverflow資料。我們後面的示例中會經常用到它。其基本原理是讀入一條stackoverflow的xml(這是一種很容易預測的格式)檔案中的記錄,然後将xml的屬性和其值儲存在一個map中。

接下來,請注意wordcountmapper類。這部分代碼會比驅動程式稍微複雜些。我們會看到大部分工作是在mapper中完成的,是以,第一個需要重點關注的就是它的父類類型:

mapper

mapper輸入的鍵、值資料類型是在作業配置的fileinputformat中定義的。預設實作是textinputformat。textinputformat的鍵是longwritable對象,表示截至目前從檔案中讀入的位元組數;其值是text對象,表示從文本中讀入的一行記錄。如果使用不同的輸入格式,那麼很可能需要改變這些鍵/值的資料類型。

直到調用stringtokenizer之前,所做的事情都是清理字元串。首先,由于原始資料中的字元串是按照xml的格式存儲的,是以需要先将字元串提取出來。然後,再剔除那些無用的标點符号。例如,hadoop!和hadoop?應該等同于hadoop。最後,對于每一個token(即記号),将會輸出該記号和數字1,即表明該記号出現了1次。架構将會接下來對這些鍵/值對進行混排和排序,然後交由reduce任務處理。

最後,我們來分析reducer代碼,這部分代碼相對簡單。對于每個鍵分組都會調用reduce函數,在本例中鍵為單詞。然後,reduce函數将疊代處理該鍵對應的所有值,即将值進行求和,在本例中值為單詞的出現次數。得到的結果就是該單詞出現的總次數。

在本例的mapper中,輸入、輸出類型是通過父類模闆定義的。和mapper一樣,reducer定義中也包括以下四個類型:輸入鍵、輸入值、輸出鍵和輸出值。輸入鍵和輸入值的資料類型必須和mapper的輸出鍵及輸出值類型一緻。輸出鍵、輸出值資料類型必須和作業配置的fileoutputformat一緻。在本例中,使用預設的textoutputformat格式,textoutputformat可以把任意兩個writable對象作為輸出。

reduce函數的很多簽名有别于map函數:reduce函數有一個iterator(疊代器),它包含的是所有的值,而不是單個值。這意味着你可以通過疊代的方式處理一個鍵所對應的所有值,而不是一次隻能處理一個。對于絕大部分mapreduce作業中的reducer來說,鍵通常都是非常重要的,但mapper中的輸入鍵則不然。

所有傳給context.write的資料最終都會寫到一個輸出檔案中。每個reducer将建立一個檔案,是以,如果想要把結果合并到一起,則還需要在最後增加一個合并它們的處理步驟。

現在我們已經掌握了一個簡單的示例,下面讓我們深入學習一些設計模式!

繼續閱讀