1、MapReduce理論
1.1、MapReduce是什麼?
MapReduce用於處理海量數據的分佈式計算框架,是Hadoop生態中的核心之一(MapReduce用於計算海量數據,HDFS用於存儲海量數據);MapReduce是谷歌公司在研究如何處理海量數據所提出的一種面向大規模數據處理的並行計算模型和方法。
1.2、MapReduce概述
MapReduce是一個計算框架,用於對大數據進行處理,它的主要思想就是“分而治之”;整個MapReduce計算過程可以分爲Map(映射)階段和Reduce(縮減階段);一個Map/Reduce 作業(job) 通常會把輸入的數據集切分爲若干獨立的數據塊,由 map任務(task)以完全並行的方式處理它們。框架會對Map的輸出先進行排序, 然後把結果輸入給Reduce任務。通常作業的輸入和輸出都會被存儲在文件系統(HDFS)中。 整個框架負責任務的調度和監控,以及重新執行已經失敗的任務。
Map/Reduce框架由一個單獨的master JobTracker 和每個集羣節點一個slave TaskTracker共同組成。master負責調度構成一個作業的所有任務,這些任務分佈在不同的slave上,master監控它們的執行,重新執行已經失敗的任務。而slave僅負責執行由master指派的任務。
應用程序至少應該指明輸入/輸出的位置(路徑),並通過實現合適的接口或抽象類提供map和reduce函數。再加上其他作業的參數,就構成了作業配置(job configuration)。然後,Hadoop的 job client提交作業(jar包/可執行程序等)和配置信息給JobTracker,後者負責分發這些軟件和配置信息給slave、調度任務並監控它們的執行,同時提供狀態和診斷信息給job-client。
一個Map/Reduce 作業的輸入和輸出類型如下所示:
(input) <k1, v1> -> map -> <k2, v2> -> combine -> <k2, v2> -> reduce -> <k3, v3> (output)
雖然Hadoop框架是用JavaTM實現的,但Map/Reduce應用程序則不一定要用 Java來寫 。
- Hadoop Streaming是一種運行作業的實用工具,它允許用戶創建和運行任何可執行程序 (例如:Shell工具)來做爲mapper和reducer。
- Hadoop Pipes是一個與SWIG兼容的C++ API (沒有基於JNITM技術),它也可用於實現Map/Reduce應用程序。
1.3、MapReduce核心功能描述
1.3.1、MapReduce執行流程
-
Step 1:從本地HDFS讀取文件輸入的內容,每個輸入文件被切分成一定大小的數據塊Block(1.0版本默認64M);
-
Step 2:每個被切分的數據塊Block會產生對應的Map任務,用戶可以定義map函數,對被切分的數據使用map函數解析成一個key/value格式的數據;
-
Step 3:對每個map任務得到的key/value格式的數據按照不同的分區,通過網絡傳輸到不同的Reduce節點;
-
Step 4:對多個map任務輸出的結果進行排序、合併,然後通過reduce函數進程處理得到最新的key/value結果;
- Step 5:將Reduce輸出的結果保存到文件系統中。
1.3.2、Map和Reduce的任務數量
在整個hadoop的MapReduce計算過程中,需要多少個map和reduce呢?
- map的任務數量
Map的任務數量通常是由輸入數據的大小決定的,一般就是所有輸入文件的總塊(block)數。
Map正常的並行規模大致是每個節點(node)大約10到100個map,對於CPU 消耗較小的map任務可以設到300個左右。由於每個任務初始化需要一定的時間,因此,比較合理的情況是map執行的時間至少超過1分鐘;
例如:輸入10TB的數據,每個塊(block)的大小是128MB,你將需要大約82,000個map來完成任務,通過 setNumMapTasks(int)可以修改map數值。
- reduce的任務數量
Reduce的任務數量建議是0.95或1.75乘以 (<no. of nodes> * mapred.tasktracker.reduce.tasks.maximum)。
用0.95,所有reduce可以在maps一完成時就立刻啓動,開始傳輸map的輸出結果。用1.75,速度快的節點可以在完成第一輪reduce任務後,可以開始第二輪,這樣可以得到比較好的負載均衡的效果。
增加reduce的數目會增加整個框架的開銷,但可以改善負載均衡,降低由於執行失敗帶來的負面影響。
2、MapReduce案例
2.1、使用shell命令演示“mapper”和“reducer”
對一個英文文本的單詞進行統計,shell命令行處理思路如下:
- 打開文件:
cat file
- 將文本的空格替換成換行符:
tr ' ' '\n'
- 將替換空格文本的單詞進行排序:
sort -k 1
- 將排序後的文本顯示每行出現單詞的數字:
uniq -c
- 將文件中的數字(value)和單詞(key)互換位置:
awk '{print $2"\t"$1}'
: - 將替換鍵值的文本相同的詞進行彙總並按大小進行排序:
sort -k2 -nr
- 將結果保存爲新的文件:
newfile
以上步驟中,第2-5步相當於mapper,第6步爲reducer。
對文本The_Man_of_Property的單詞進行統計;
$ cat The_Man_of_Property |tr ' ' '\n'|sort -k 1|uniq -c|awk '{print $2 "\t"$1}'|sort -k2 -nr|head -n 20
the 5144
of 3407
to 2782
and 2573
a 2543
he 2139
his 1912
was 1702
in 1694
had 1526
that 1273
with 1029
her 1020
— 931
at 815
for 765
not 723
she 711
He 695
it 689
以上顯示了前20行的結果,需要將計算的結果保存下來,追加到文件即可
$ cat The_Man_of_Property |tr ' ' '\n'|sort -k 1|uniq -c|awk '{print $2 "\t"$1}'|sort -k2 -nr > wordcountretult.txt
2.2、使用Streaming運行MapReducer程序
使用python分別編寫mapper和reducer,通過Hadoop Streaming運行程序;
2.2.1 Hadoop Streaming
Hadoop streaming是Hadoop的一個工具, 它幫助用戶創建和運行一類特殊的map/reduce作業, 這些特殊的map/reduce作業是由一些可執行文件或腳本文件充當mapper或者reducer。例如:
$HADOOP_HOME/bin/hadoop jar $HADOOP_HOME/hadoop-streaming.jar \
-input myInputDirs \
-output myOutputDir \
-mapper /bin/cat \
-reducer /bin/wc
-file /bin/cat
-file /bin/cat
- -input:輸入文件的路徑,從HDFS上讀取文件
- -output:計算結果的輸出路徑,保存到HDFS上
- -mapper:可執行的程序或腳本
- -reducer:可執行的程序或腳本
2.2.2 wordcount by Python
使用Python編寫“mapper.py”和“reducer.py”:
- mapper.py從標準輸入讀取文件;
- mapper.py將文本的單詞處理成key/value的形式;
- reducer.py的標準輸入就是mapper.py的輸出;
- reducer.py將讀入的單詞進行歸併整理,最終梳理爲所有單詞的彙總輸出到文件;
注:以下的程序運行在hadoop 1.2.1上;
2.2.2.1、mapper by python
vim mapper.py
#!/usr/bin/env python
import sys
for line in sys.stdin:
line = line.strip()
words = line.split()
for word in words:
print "%s\t%s" %(word, 1)
從標準輸入讀入文件,遍歷每一行字符;並對每一行字符去除特殊字符,然後以空格作爲分隔符進行遍歷輸出word:1
的key/value的格式;
文本The_Man_of_Property執行maper.py程序,顯示前10行:
$ cat The_Man_of_Property |python mapper.py |head
Preface 1
“The 1
Forsyte 1
Saga” 1
was 1
the 1
title 1
originally 1
destined 1
for 1
2.2.2.2、reducer by python
mapper輸出作爲reducer的輸入,因此,reducer需要對word:1
的格式進行處理,排序、相同單詞歸併,最終輸出每個單詞的彙總數據;
vim reducer.py
#!/usr/bin/env python
import sys
current_word = None
word_sum = 0
for line in sys.stdin:
word_list = line.strip().split('\t')
if len(word_list) < 2:
continue
word = word_list[0].strip()
word_value = word_list[1].strip()
if current_word == None:
current_word = word
if current_word != word:
print "%s\t%s" %(current_word, str(word_sum))
current_word = word
word_sum = 0
word_sum += int(word_value)
print "%s\t%s" %(current_word, str(word_sum))
word_sum = 0
reducer從mapper標準輸出讀入數據,對相同單詞進行彙總;
2.2.2.3、本地測試mapper和reducer
在本地上先測試的流程方法:cat inputfile|mapper.py|sort|reducer.py
cat The_Man_of_Property |python mapper.py |sort -k1|python reducer.py
2.2.2.4、hadoop(hadoop-1.2.1)上測試
- 文本The_Man_of_Property上傳到HDFS上
- mapper.py和reducer.py增加執行權限
hadoop fs -mkdir /user/input
hadoop fs -mkdir /user/output
hadoop fs -copyFromLocal The_Man_of_Property /user/input/
chmod +x mapper.py reducer.py
- 編寫運行streaming的shell腳本:
vim run.sh
#!/bin/bash
HADOOP_CMD="/home/hadoop/app/hadoop/hadoop-1.2.1/bin/hadoop"
STREAM_JAR_PATH="/home/hadoop/app/hadoop/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"
INPUT_FILE_PATH="/user/input/The_Man_of_Property"
OUTPUT_FILE_PATH="/user/output/output00"
#
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH \
-output $OUTPUT_FILE_PATH \
-mapper "python mapper.py" \
-reducer "python reducer.py" \
-file ./mapper.py \
-file ./reducer.py
- 給
run.sh
增加執行權限,運行程序:
chmod +x run.sh
./run.sh
packageJobJar: [./mapper.py, ./reducer.py, /home/hadoop/app/hadoop/hadoop-1.2.1/tmp/hadoop-unjar8638446041851116131/] [] /tmp/streamjob3031434578661633957.jar tmpDir=null
18/01/16 13:49:17 INFO util.NativeCodeLoader: Loaded the native-hadoop library
18/01/16 13:49:17 WARN snappy.LoadSnappy: Snappy native library not loaded
18/01/16 13:49:17 INFO mapred.FileInputFormat: Total input paths to process : 1
18/01/16 13:49:17 INFO streaming.StreamJob: getLocalDirs(): [/home/hadoop/app/hadoop/hadoop-1.2.1/tmp/mapred/local]
18/01/16 13:49:17 INFO streaming.StreamJob: Running job: job_201801150959_0008
18/01/16 13:49:17 INFO streaming.StreamJob: To kill this job, run:
18/01/16 13:49:17 INFO streaming.StreamJob: /home/hadoop/app/hadoop/hadoop-1.2.1/libexec/../bin/hadoop job -Dmapred.job.tracker=http://192.168.30.50:9001 -kill job_201801150959_0008
18/01/16 13:49:17 INFO streaming.StreamJob: Tracking URL: http://hadoop-master:50030/jobdetails.jsp?jobid=job_201801150959_0008
18/01/16 13:49:18 INFO streaming.StreamJob: map 0% reduce 0%
18/01/16 13:49:25 INFO streaming.StreamJob: map 100% reduce 0%
18/01/16 13:49:33 INFO streaming.StreamJob: map 100% reduce 33%
18/01/16 13:49:36 INFO streaming.StreamJob: map 100% reduce 100%
18/01/16 13:49:38 INFO streaming.StreamJob: Job complete: job_201801150959_0008
18/01/16 13:49:38 INFO streaming.StreamJob: Output: /user/output/output00
- 查看輸出結果
hadoop fs -ls /user/output/output00 Found 3 items -rw-r--r-- 2 hadoop supergroup 0 2018-01-16 13:49 /user/output/output00/_SUCCESS drwxr-xr-x - hadoop supergroup 0 2018-01-16 13:49 /user/output/output00/_logs -rw-r--r-- 2 hadoop supergroup 181530 2018-01-16 13:49 /user/output/output00/part-00000
計算的結果輸出到文件:/user/output/output00/part-00000,可以將文件下載到本地,和使用shell命令處理的結果是相同的。
$ hadoop fs -get /user/output/output00/part-00000 ./ $ cat part-00000 |sort -k2 -nr|head the 5144 of 3407 to 2782 and 2573 a 2543 he 2139 his 1912 was 1702 in 1694 had 1526
$ head wordcountretult.txt
the 5144
of 3407
to 2782
and 2573
a 2543
he 2139
his 1912
was 1702
in 1694
had 1526
3、遇到的問題
使用streaming運行python編寫的mapper和reducer的是出現瞭如下錯誤:
ERROR streaming.StreamJob: Job not successful. Error: # of failed Map Tasks exceeded allowed limit. FailedCount: 1. LastFailedTask
因爲本地驗證正常,可以確定的是mapper.py和reducer.py程序應該沒有問題;使用 -file 指定mapper和reducer的路徑(相對路徑或者絕對路徑都可以)再一次運行,沒有報錯了。