從這篇文章開始,我會開始系統性地輸出在大資料踩坑過程中的積累,後面會涉及到實戰專案的具體操作,目前的規劃是按照系列來更新,力爭做到一個系列在5
篇文章之內總結出最核心的乾貨,如果是涉及到理論方面的文章,會以畫圖的方式來講解,如果是涉及到操作方面,會以實際的程式碼來演示。
這篇是MapReduce
系列的第一篇,初識MapReduce
的應用場景,在文章後面會有關於程式碼的演示。
前言
Hadoop
作為Apache
旗下的一個以Java
語言實現的分散式計算開源框架,其由兩個部分組成,一個是分散式的檔案系統HDFS
,另一個是批處理計算框架MapReduce
。這篇文章作為MapReduce
系列的第一篇文章,會從MapReduce
的產生背景、框架的計算流程、應用場景和演示Demo
來講解,主要是讓大家對MapReduce
的這個批計算框架有個初步的瞭解及簡單的部署和使用。
目錄
MapReduce
的產生背景
MapReduce
的計算流程
MapReduce
的框架架構
MapReduce
的生命週期
應用場景
演示Demo
MapReduce的產生背景
Google
在2004年的時候在 MapReduce: Simplified Data Processing on Large Clusters 這篇論文中提出了MapReduce
的功能特性和設計理念,設計MapReduce
的出發點就是為了解決如何把大問題分解成獨立的小問題,再並行解決。例如,MapReduce
的經典使用場景之一就是對一篇長文進行詞頻統計,統計過程就是先把文章分為一句一句,然後進行分割,最後進行詞的數量統計。
MapReduce的架構圖
這裡的Client和TaskTracker我都使用一個來簡化了,在實際中是會有很個Client和TaskTracker的。我們來講解下不同的元件作用
Client
Client
的含義是指使用者使用MapReduce
程式通過Client
來提交任務到Job Tracker
上,同時使用者也可以使用Client
來檢視一些作業的執行狀態。
Job Tracker
這個負責的是資源監控和作業排程。JobTracker
會監控著TaskTracker
和作業的健康狀況,會把失敗的任務轉移到其他節點上,同時也監控著任務的執行進度、資源使用量等情況,會把這些訊息通知任務排程器,而排程器會在資源空閒的時候選擇合適的任務來使用這些資源。
任務排程器是一個可插拔的模組,使用者可以根據自己的需要來設計相對應的排程器。
TaskTracker
TaskTracker
會週期性地通過Hearbeat
來向Job Tracker
彙報自己的資源使用情況和任務的執行進度。會接受來自於JobTaskcker
的指令來執行操作(例如啟動新任務、殺死任務之類的)。
在TaskTracker
中通過的是slot
來進行等量劃分一個節點上資源量,只用Task
獲得slot
的時候才有機會去執行。排程器的作用就是進行將空閒的slot
分配給Task
使用,可以配置slot
的數量來進行限定Task上的併發度。
Task
Task分為Map Task
和Reduce Task
,在MapReduce
中的 split
就是一個 Map Task
,split
的大小可以設定的,由 mapred.max.spilt.size
引數來設定,預設是 Hadoop
中的block
的大小,在Hadoop 2.x
中預設是128M
,在Hadoop 1.x
中預設是64M
。
在Task
中的設定可以這麼設定,一般來講,會把一個檔案設定為一個split
,如果是小檔案,那麼就會存在很多的Map Task
,這是特別浪費資源的,如果split
切割的資料塊的量大,那麼會導致跨節點去獲取資料,這樣也是消耗很多的系統資源的。
MapReduce的生命週期
一共分為5個步驟:
- 作業的提交和初始化
由使用者提交作業之前,需要先把檔案上傳到HDFS
上,JobClient
使用upload
來載入關於打包好的jar
包,JobClient
會RPC
建立一個JobInProcess
來進行管理任務,並且建立一個TaskProcess
來管理控制關於每一個Task
。
- JobTracker排程任務
JobTracker
會排程和管理任務,一發現有空閒資源,會按照一個策略選擇一個合適的任務來使用該資源。
任務排程器有兩個點:一個是保證作業的順利執行,如果有失敗的任務時,會轉移計算任務,另一個是如果某一個Task的計算結果落後於同一個Task的計算結果時,會啟動另一個Task來做計算,最後去計算結果最塊的那個。
- 任務執行環境
TaskTracker會為每一個Task來準備一個獨立的JVM從而避免不同的Task在執行過程中的一些影響,同時也使用了作業系統來實現資源隔離防止Task濫用資源。
- 執行任務
每個Task的任務進度通過RPC來彙報給TaskTracker,再由TaskTracker彙報給JobTracker。
- 任務結束,寫入輸出的檔案到HDFS中。
MapReduce 的計算流程
先來看一張圖,系統地瞭解下 MapReduce
的運算流程。
為了方便大家理解,重新畫了一張新的圖,演示的是關於如何進行把一個長句進行分割,最後進行詞頻的統計(已忽略掉標點符號)。
整個過程就是先讀取檔案,接著進行split
切割,變成一個一個的詞,然後進行 map task
任務,排列出所有詞的統計量,接著 sorting
排序,按照字典序來排,接著就是進行 reduce task
,進行了詞頻的彙總,最後一步就是輸出為檔案。例如圖中的 spacedong
就出現了兩次。
其中對應著的是 Hadoop Mapreduce
對外提供的五個可程式設計元件,分別是InputFormat
、Mapper
、Partitioner
、Reduce
和OutputFormat
,後續的文章會詳細講解這幾個元件。
用一句話簡單地總結就是,Mapreduce
的運算過程就是進行拆解-排序-彙總,解決的就是統計的問題,使用的思想就是分治的思想。
MapReduce的應用場景
MapReduce
的產生是為了把某些大的問題分解成小的問題,然後解決小問題後,大問題也就解決了。那麼一般有什麼樣的場景會運用到這個呢?那可多了去,簡單地列舉幾個經典的場景。
計算URL
的訪問頻率
搜尋引擎的使用中,會遇到大量的URL的訪問,所以,可以使用 MapReduce
來進行統計,得出(URL
,次數)結果,在後續的分析中可以使用。
倒排索引
Map
函式去分析檔案格式是(詞,文件號)的列表,Reduce
函式就分析這個(詞,文件號),排序所有的文件號,輸出(詞,list
(文件號)),這個就可以形成一個簡單的倒排索引,是一種簡單的演算法跟蹤詞在文件中的位置。
Top K 問題
在各種的文件分析,或者是不同的場景中,經常會遇到關於 Top K
的問題,例如輸出這篇文章的出現前5
個最多的詞彙。這個時候也可以使用 MapReduce
來進行統計。
演示Demo
今天的程式碼演示從Python
和Java
兩個版本的演示,Python
版本的話便是不使用封裝的包,Java
版本的話則是使用了Hadoop
的封裝包。接下來便進行演示一個MapReduce
的簡單使用,如何進行詞彙統計。
Java
版本程式碼
- 先是準備一個資料集,包含著已經切割好的詞彙,這裡我們設定檔案的格式是
txt
格式的。檔名是WordMRDemo.txt
,內容是下面簡短的一句話,以空格分割開:
hello my name is spacedong welcome to the spacedong thank you
- 引入
Hadoop
的依賴包
//這裡使用的是2.6.5的依賴包,你可以使用其他版本的
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.6.5</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.6.5</version>
</dependency>
複製程式碼
- 新建
WordMapper.java
檔案,程式碼的作用是進行以空格的形式進行分詞。
public class WordMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
@Override
protected void map(LongWritable key, Text value, Mapper.Context context)
throws java.io.IOException, InterruptedException {
String line = value.toString();
//StringTokenizer預設按照空格來切
StringTokenizer st = new StringTokenizer(line);
while (st.hasMoreTokens()) {
String world = st.nextToken();
//map輸出
context.write(new Text(world), new IntWritable(1));
}
}
}
複製程式碼
- 新建
WordReduce.java
檔案,作用是進行詞彙的統計。
public class WordReduce extends Reducer<Text, IntWritable, Text, IntWritable> {
@Override
protected void reduce(Text key, Iterable<IntWritable> iterator, Context context)
throws java.io.IOException ,InterruptedException {
int sum = 0 ;
for(IntWritable i:iterator){
sum+=i.get();
}
context.write(key, new IntWritable(sum));
}
}
複製程式碼
- 新建
WordMRDemo.java
檔案,作用是執行Job
,開始分析句子。
public class WordMRDemo {
public static void main(String[] args) {
Configuration conf = new Configuration();
//設定mapper的配置,既就是hadoop/conf/mapred-site.xml的配置資訊
conf.set("mapred.job.tracker", "hadoop:9000");
try {
//新建一個Job工作
Job job = new Job(conf);
//設定執行類
job.setJarByClass(WordMRDemo.class);
//設定要執行的mapper類
job.setMapperClass(WordMapper.class);
//設定要執行的reduce類
job.setReducerClass(WordReduce.class);
//設定輸出key的型別
job.setMapOutputKeyClass(Text.class);
//設定輸出value的型別
job.setMapOutputValueClass(IntWritable.class);
//設定ruduce任務的個數,預設個數為一個(一般reduce的個數越多效率越高)
//job.setNumReduceTasks(2);
//mapreduce 輸入資料的檔案/目錄,注意,這裡可以輸入的是目錄。
FileInputFormat.addInputPath(job, new Path("F:\\BigDataWorkPlace\\data\\input"));
//mapreduce 執行後輸出的資料目錄,不能預先存在,否則會報錯。
FileOutputFormat.setOutputPath(job, new Path("F:\\BigDataWorkPlace\\data\\out"));
//執行完畢退出
System.exit(job.waitForCompletion(true) ? 0 : 1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
複製程式碼
- 最後執行
WordMRDemo.java
檔案,然後得到的結果是out
資料夾內的內容,它長這個樣子:
開啟part-r-00000
檔案的內容如下
Python程式碼版本
- 新建
map.py
檔案,進行詞彙的切割。
for line in sys.stdin:
time.sleep(1000)
ss = line.strip().split(' ')
for word in ss:
print '\t'.join([word.strip(), '1'])
複製程式碼
- 新建
red.py
檔案,進行詞彙的統計。
cur_word = None
sum = 0
for line in sys.stdin:
ss = line.strip().split('\t')
if len(ss) != 2:
continue
word, cnt = ss
if cur_word == None:
cur_word = word
if cur_word != word:
print '\t'.join([cur_word, str(sum)])
cur_word = word
sum = 0
sum += int(cnt)
print '\t'.join([cur_word, str(sum)])
複製程式碼
- 新建
run.sh
檔案,直接執行即可。
HADOOP_CMD="/usr/local/src/hadoop-2.6.5/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.5/share/hadoop/tools/lib/hadoop-streaming-2.6.5.jar"
INPUT_FILE_PATH_1="/test.txt"
OUTPUT_PATH="/output"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
# Step 1.
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH_1 \
-output $OUTPUT_PATH \
-mapper "python map.py" \
-reducer "python red.py" \
-file ./map.py \
-file ./red.py
複製程式碼
以上的是演示demo
的核心程式碼,完整的程式碼可以上github
的程式碼倉庫上獲取。
倉庫地址為:https://github.com/spacedong/bigDataNotes
以上的文章是MapReduce
系列的第一篇,下篇預告是MapReduce的程式設計模型
,敬請期待!
參考資料:
Hadoop的技術內幕:深入解析MapReduce架構設計及實現原理