天天看點

大資料基石——Hadoop與MapReduce

簡單介紹hadoop與MapReduce

本文始發于個人公衆号:TechFlow

大資料基石——Hadoop與MapReduce

近兩年AI成了最火熱領域的代名詞,各大高校紛紛推出了人工智能專業。但其實,人工智能也好,還是前兩年的深度學習或者是機器學習也罷,都離不開底層的資料支援。對于動辄數以TB記級别的資料,顯然正常的資料庫是滿足不了要求的。今天,我們就來看看大資料時代的幕後英雄——Hadoop。

Hadoop這個關鍵詞其實有兩重含義,最早它其實指的就是單純的分布式計算系統。但是随着時代的發展,Hadoop系統擴大,如今hadoop已經是成了一個完整的技術家族。從底層的分布式檔案系統(HDFS)到頂層的資料解析運作工具(Hive、Pig),再到分布式系統協調服務(ZooKeeper)以及分布式資料庫(HBase),都屬于Hadoop家族,幾乎涵蓋了大半大資料的應用場景。在Spark沒有流行之前,Hadoop一直是大資料應用中的絕對主流,即使是現在,依舊有大量的中小型公司,還是依靠Hadoop搭建大資料系統。

如今的Hadoop雖然家族龐大,但是早年Hadoop的結構非常簡單,幾乎隻有兩塊,一塊是分布式檔案系統,這個是整個資料的支撐,另一個就是MapReduce算法。

大資料基石——Hadoop與MapReduce

分布式檔案系統

大資料時代,資料的量級大規模增長,動辄以TB甚至PB計。對于這麼海量的資料,如果我們還使用正常的方法是非常困難的。因為即使是 O(n) 的算法,将所有的資料周遊一遍,所消耗的時間也肯定是以小時計,這顯然是不能接受的。不僅如此,像是MySQL這樣的資料庫對于資料規模也是有限制的,一旦資料規模巨大,超過了資料庫的承載能力,那幾乎是系統級的噩夢(重要的資料不能丢棄,但是現在的系統無法支撐)。

大資料基石——Hadoop與MapReduce

既然我們把資料全部存儲在一起,會導緻系統問題,那麼我們可不可以把資料分成很多份分别存儲,當我們需要處理這些資料的時候,我們對這些分成許多小份的資料分别處理,最後再合并在一起?

答案當然是可行的,Hadoop的檔案系統正是基于這個思路。

在HDFS當中,将資料分割成一個一個的小份。每個小份叫做一個存儲塊,每個存儲塊為64MB。這樣一個巨大的檔案會被打散存儲在許多存儲塊當中。當我們需要操作這些資料的時候,Hadoop會同時起動許多個執行器(executor)來并發執行這些存儲塊。理論上來說,執行器的數量越多,執行的速度也就越快。隻要我們有足夠多的執行器,就可以在短時間内完成海量資料的計算工作。

但是有一個小問題,為什麼每個存儲塊偏偏是64MB,而不是128MB或者256MB呢?

原因也很簡單,因為資料存儲在硬碟上,當我們查找資料的時候,CPU其實是不知道資料究竟存放在什麼地方的。需要有一個專門的程式去查找資料的位置,這個過程被稱為尋址。尋址的時候會伴随着硬碟的高速旋轉。硬碟的旋轉速度是有限的,自然我們查找檔案的速度也會存在瓶頸。如果存儲塊太小,那麼存儲塊的數量就會很多,我們尋址的時間就會變長。

大資料基石——Hadoop與MapReduce

如果存儲塊設定得大一些行不行?也不行,因為我們在執行的時候,需要把存儲塊的資料拷貝到執行器的記憶體裡執行。這個拷貝伴随着讀寫和網絡傳輸的操作,傳輸資料同樣耗時不少。存儲塊過大,會導緻讀寫的時間過長,同樣不利于系統的性能。根據業内的說法,希望尋址的耗時占傳輸時間的1%,目前的網絡帶寬最多可以做到100MB/s,根據計算,每個存儲塊大約在100MB左右最佳。也許是程式員為了湊整,是以選了64MB這個大小。

目前為止,我們已經搞清楚了Hadoop内部的資料存儲的原理。那麼,Hadoop又是怎麼并發計算的呢?這就下一個關鍵詞——MapReduce出場了。

MapReduce

嚴格說起來MapReduce并不是一種算法, 而是一個計算思想。它由map和reduce兩個階段組成。

大資料基石——Hadoop與MapReduce

先說map,MapReduce中的map和Java或者是C++以及一些其他語言的map容器不同,它表示的意思是映射。負責執行map操作的機器(稱作mapper)從HDFS當中拿到資料之後,會對這些資料進行處理,從其中提取出我們需要用到的字段或者資料,将它組織成key->value的結構,進行傳回。

為什麼要傳回key->value的結構呢?直接傳回我們要用到的value不行嗎?

不行,因為在map和reduce中間,Hadoop會根據key值進行排序,将key值相同的資料歸并到一起之後,再發送給reducer執行。也就是說,key值相同的資料會被同一個reducer也就是同一台機器處理,并且key相同的資料連續排列。reducer做的是通過mapper拿到的資料,生成我們最終需要的結果。

Sample

這個過程應該不難了解, 但是初學者可能面臨困惑,為什麼一開始的時候,要處理成key-value結構的呢?為什麼又要将key值相同的資料放入一個reducer當中呢,這麼做有什麼意義?

這裡,我們舉一個例子,就清楚了。

MapReduce有一個經典的問題,叫做wordCount,顧名思義就是給定一堆文本,最後計算出文本當中每個單詞分别出現的次數。Map階段很簡單,我們周遊文本當中的單詞,每遇到一個單詞,就輸出單詞和數字1。寫成代碼非常簡單:

def map(text): for line in text: words = line.split(' ') for w in words: print(w, 1)

這樣當然還是不夠的,我們還需要把相同的單詞聚合起來,清點一下看看究竟出現了多少次,這個時候就需要用到reducer了。reducer也很簡單,我們讀入的是map輸出的結果。由于key相同的資料都會進入同一個reducer當中,是以我們不需要擔心遺漏,隻需要直接統計就行:

def reduce(text): wordNow = None totCount = 0 for line in text: elements = line.split(' ') word, count = elements[0], int(elements[1]) # 碰到不同的key,則輸出之前的單詞以及數量 if word != wordNow: if wordNow is not None: print(wordNow, totCount) wordNow = word totCount = 1 #否則,更新totCount else: totCount += count

如果我們map的結果不是key-value結構,那麼Hadoop就沒辦法根據key進行排序,并将key相同的資料歸并在一起。那麼我們在reduce的時候,同一個單詞就可能出現在不同的reducer當中,這樣的結果顯然是不正确的。

當然,如果我們隻做一些簡單的操作,也可以舍棄reduce階段,隻保留map産出的結果。

現在看MapReduce的思想其實并不複雜,但是當年大資料還未興起的時候,MapReduce橫空出世,既提升了計算性能,又保證了結果的準确。一舉解決了大規模資料并行計算的問題,回想起來,應該非常驚豔。雖然如今技術更新,尤其是Spark的流行,搶走了Hadoop許多榮光。但MapReduce的思想依舊在許多領域廣泛使用,比如Python就支援類似的MapReduce操作,允許使用者自定義map和reduce函數,對資料進行并行處理。

不過,MapReduce也有短闆,比如像是資料庫表join的操作通過MapReduce就很難實作。而且相比于後來的Hive以及Spark SQL來說,MapReduce的編碼複雜度還是要大一些。但不管怎麼說,瑕不掩瑜,對于初學者而言,它依舊非常值得我們深入了解。

掃碼關注,擷取更多文章:

大資料基石——Hadoop與MapReduce

繼續閱讀