爲什麼說MapReduce既是編程模型又是計算框架?

在Hadoop問世之前,其實已經有了分佈式計算,只是那個時候的分佈式計算都是專用的系統,只能專門處理某一類計算,比如進行大規模數據的排序。很顯然,這樣的系統無法複用到其他的大數據計算場景,每一種應用都需要開發與維護專門的系統。而Hadoop MapReduce的出現,使得大數據計算通用編程成爲可能。我們只要遵循MapReduce編程模型編寫業務處理邏輯代碼,就可以運行在Hadoop分佈式集羣上,無需關心分佈式計算是如何完成的。也就是說,我們只需要關心業務邏輯,不用關心繫統調用與運行環境,這和我們目前的主流開發方式是一致的。

請你先回憶一下,在前面專欄第4期我們討論過,大數據計算的核心思路是移動計算比移動數據更划算。既然計算方法跟傳統計算方法不一樣,移動計算而不是移動數據,那麼用傳統的編程模型進行大數據計算就會遇到很多困難,因此Hadoop大數據計算使用了一種叫作MapReduce的編程模型。

其實MapReduce編程模型並不是Hadoop原創,甚至也不是Google原創,但是Google和Hadoop創造性地將MapReduce編程模型用到大數據計算上,立刻產生了神奇的效果,看似複雜的各種各樣的機器學習、數據挖掘、SQL處理等大數據計算變得簡單清晰起來。

今天我們就來聊聊Hadoop解決大規模數據分佈式計算的方案——MapReduce。

在我看來,MapReduce既是一個編程模型,又是一個計算框架。也就是說,開發人員必須基於MapReduce編程模型進行編程開發,然後將程序通過MapReduce計算框架分發到Hadoop集羣中運行。我們先看一下作爲編程模型的MapReduce。

爲什麼說MapReduce是一種非常簡單又非常強大的編程模型?

簡單在於其編程模型只包含Map和Reduce兩個過程,map的主要輸入是一對<Key, Value>值,經過map計算後輸出一對<Key, Value>值;然後將相同Key合併,形成<Key, Value集合>;再將這個<Key, Value集合>輸入reduce,經過計算輸出零個或多個<Key, Value>對。

同時,MapReduce又是非常強大的,不管是關係代數運算(SQL計算),還是矩陣運算(圖計算),大數據領域幾乎所有的計算需求都可以通過MapReduce編程來實現。

下面,我以WordCount程序爲例,一起來看下MapReduce的計算過程。

WordCount主要解決的是文本處理中詞頻統計的問題,就是統計文本中每一個單詞出現的次數。如果只是統計一篇文章的詞頻,幾十KB到幾MB的數據,只需要寫一個程序,將數據讀入內存,建一個Hash表記錄每個詞出現的次數就可以了。這個統計過程你可以看下面這張圖。

如果用Python語言,單機處理WordCount的代碼是這樣的。

# 文本前期處理
strl_ist = str.replace('\n', '').lower().split(' ')
count_dict = {}
# 如果字典裏有該單詞則加1,否則添加入字典
for str in strl_ist:
if str in count_dict.keys():
    count_dict[str] = count_dict[str] + 1
    else:
        count_dict[str] = 1

簡單說來,就是建一個Hash表,然後將字符串裏的每個詞放到這個Hash表裏。如果這個詞第一次放到Hash表,就新建一個Key、Value對,Key是這個詞,Value是1。如果Hash表裏已經有這個詞了,那麼就給這個詞的Value + 1。

小數據量用單機統計詞頻很簡單,但是如果想統計全世界互聯網所有網頁(數萬億計)的詞頻數(而這正是Google這樣的搜索引擎的典型需求),不可能寫一個程序把全世界的網頁都讀入內存,這時候就需要用MapReduce編程來解決。

WordCount的MapReduce程序如下。

public class WordCount {

  public static class TokenizerMapper
       extends Mapper<Object, Text, Text, IntWritable>{

    private final static IntWritable one = new IntWritable(1);
    private Text word = new Text();

    public void map(Object key, Text value, Context context
                    ) throws IOException, InterruptedException {
      StringTokenizer itr = new StringTokenizer(value.toString());
      while (itr.hasMoreTokens()) {
        word.set(itr.nextToken());
        context.write(word, one);
      }
    }
  }

  public static class IntSumReducer
       extends Reducer<Text,IntWritable,Text,IntWritable> {
    private IntWritable result = new IntWritable();

    public void reduce(Text key, Iterable<IntWritable> values,
                       Context context
                       ) throws IOException, InterruptedException {
      int sum = 0;
      for (IntWritable val : values) {
        sum += val.get();
      }
      result.set(sum);
      context.write(key, result);
    }
  }
}

你可以從這段代碼中看到,MapReduce版本WordCount程序的核心是一個map函數和一個reduce函數。

map函數的輸入主要是一個<Key, Value>對,在這個例子裏,Value是要統計的所有文本中的一行數據,Key在一般計算中都不會用到。

public void map(Object key, Text value, Context context
                    )

map函數的計算過程是,將這行文本中的單詞提取出來,針對每個單詞輸出一個<word, 1>這樣的<Key, Value>對。

MapReduce計算框架會將這些<word , 1>收集起來,將相同的word放在一起,形成<word , <1,1,1,1,1,1,1…>>這樣的<Key, Value集合>數據,然後將其輸入給reduce函數。

public void reduce(Text key, Iterable<IntWritable> values,
                       Context context
                       ) 

這裏reduce的輸入參數Values就是由很多個1組成的集合,而Key就是具體的單詞word。

reduce函數的計算過程是,將這個集合裏的1求和,再將單詞(word)和這個和(sum)組成一個<Key, Value>,也就是<word, sum>輸出。每一個輸出就是一個單詞和它的詞頻統計總和。

一個map函數可以針對一部分數據進行運算,這樣就可以將一個大數據切分成很多塊(這也正是HDFS所做的),MapReduce計算框架爲每個數據塊分配一個map函數去計算,從而實現大數據的分佈式計算。

假設有兩個數據塊的文本數據需要進行詞頻統計,MapReduce計算過程如下圖所示。

以上就是MapReduce編程模型的主要計算過程和原理,但是這樣一個MapReduce程序要想在分佈式環境中執行,並處理海量的大規模數據,還需要一個計算框架,能夠調度執行這個MapReduce程序,使它在分佈式的集羣中並行運行,而這個計算框架也叫MapReduce。

所以,當我們說MapReduce的時候,可能指編程模型,也就是一個MapReduce程序;也可能是指計算框架,調度執行大數據的分佈式計算。關於MapReduce計算框架,我們下期再詳細聊。

小結

總結一下,今天我們學習了MapReduce編程模型。這個模型既簡單又強大,簡單是因爲它只包含Map和Reduce兩個過程,強大之處又在於它可以實現大數據領域幾乎所有的計算需求。這也正是MapReduce這個模型令人着迷的地方。

說起模型,我想跟你聊聊我的體會。

模型是人們對一類事物的概括與抽象,可以幫助我們更好地理解事物的本質,更方便地解決問題。比如,數學公式是我們對物理與數學規律的抽象,地圖和沙盤是我們對地理空間的抽象,軟件架構圖是軟件工程師對軟件系統的抽象。

通過抽象,我們更容易把握事物的內在規律,而不是被紛繁複雜的事物表象所迷惑,更進一步深刻地認識這個世界。通過抽象,伽利略發現力是改變物體運動的原因,而不是使物體運動的原因,爲全人類打開了現代科學的大門。

這些年,我自己認識了很多優秀的人,他們各有所長、各有特點,但是無一例外都有個共同的特徵,就是對事物的洞察力。他們能夠穿透事物的層層迷霧,直指問題的核心和要害,不會猶豫和迷茫,輕鬆出手就搞定了其他人看起來無比艱難的事情。有時候光是看他們做事就能感受到一種美感,讓人意醉神迷。

這種洞察力就是來源於他們對事物的抽象能力,雖然我不知道這種能力緣何而來,但是見識了這種能力以後,我也非常渴望擁有對事物的抽象能力。所以在遇到問題的時候,我就會停下來思考:這個問題爲什麼會出現,它揭示出來背後的規律是什麼,我應該如何做。甚至有時候會把這些優秀的人帶入進思考:如果是戴老師、如果是潘大俠,他會如何看待、如何解決這個問題。通過這種不斷地訓練,雖然和那些最優秀的人相比還是有巨大的差距,但是仍然能夠感受到自己的進步,這些小小的進步也會讓自己產生大大的快樂,一種不荒廢光陰、沒有虛度此生的感覺。

我希望你也能夠不斷訓練自己,遇到問題的時候,停下來思考一下:這些現象背後的規律是什麼。有時候並不需要多麼艱深的思考,僅僅就是停一下,就會讓你察覺到以前不曾注意到的一些情況,進而發現事物的深層規律。這就是洞察力。

思考題

對於這樣一張數據表

如果存儲在HDFS中,每一行記錄在HDFS對應一行文本,文本格式是

1,25
2,25
1,32
2,25

根據上面WordCount的示例,請你寫一個MapReduce程序,得到下面這條SQL的計算結果。

SELECT pageid, age, count(1) FROM pv_users GROUP BY pageid, age;

TIPS:如何用MapReduce實現SQL計算,我們在後面還會進一步討論。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章