大數據學習之 MapReduce基礎及第一個程序 WordCount
MapReduce
英語四六級
定義
MapReduce是一個分佈式運算程序的編程框架,是用戶開發“基於hadoop的數據分析應用”的核心框架。
MapReduce核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成爲一個完整的分佈式運算程序,併發運行在一個hadoop集羣上。
優缺點
優點:
- MapReduce易於編程。它簡單的實現一些接口,就可以完成一個分佈式程序,這個分佈式程序可以分佈到大量廉價的PC機器上運行。也就是說你寫一個分佈式程序,跟寫一個簡單的串行程序是一模一樣的。就是因爲這個特點使得MapReduce編程變得非常流行。
- 良好的擴展性。當你的計算資源不能得到滿足的時候,你可以通過簡單的增加機器來擴展它的計算能力。
- 高容錯性。MapReduce設計的初衷就是使程序能夠部署在廉價的PC機器上,這就要求它具有很高的容錯性。
- 適合PB級以上的海量數據的離線處理。重點是PB級別和離線處理,說明它適合數據量巨大的離線處理而不適合在線處理。比如像毫秒級別的返回一個結果,MapReduce很難做到。
缺點:
MapReduce不擅長做實時計算、流式計算、DAG(有向圖)計算。
實時計算:MapReduce無法像Mysql一樣,在毫秒或者秒級內返回結果。
流式計算:流式計算的輸入數據是動態的,而MapReduce的輸入數據集是靜態的,不能動態變化。這是因爲MapReduce自身的設計特點決定了數據源必須是靜態的。
DAG(有向圖)計算:多個應用程序存在依賴關係,後一個應用程序的輸入爲前一個的輸出。在這種情況下,MapReduce並不是不能做,而是使用後,每個MapReduce作業的輸出結果都會寫入到磁盤,會造成大量的磁盤IO,導致性能非常的低下。
MapReduce編程思想
分佈式的運算往往需要分成至少兩個階段;
第一個階段爲maptask,是併發實例,完全並行執行,互不相干;
第二個階段爲reducetask,也是併發實例,互不相干,但是處理的數據依賴於上一階段的maptask併發實例的輸出;
MapReduce編程模型只能包含一個map階段和一個reduce階段,如果用戶的業務邏輯非常複雜,那就只能寫多個MapReduce程序,串行運行。
MapReduce進程
一個完整的MapReduce程序在分佈式運行時有三類實例進程:
1)MrAppMaster:負責整個程序的過程調度及狀態協調。
2)MapTask:負責map階段的整個數據處理流程。
3)ReduceTask:負責reduce階段的整個數據處理流程。
編碼規範
用戶編寫的程序分成三個部分:Mapper,Reducer,Driver(提交運行mr程序的客戶端)
1)Mapper階段
(1)用戶自定義的Mapper要繼承自己的父類
(2)Mapper的輸入數據是KV對的形式(KV的類型可自定義)
(3)Mapper中的業務邏輯寫在map()方法中
(4)Mapper的輸出數據是KV對的形式(KV的類型可自定義)
(5)map()方法(maptask進程)對每一個<K,V>調用一次
2)Reducer階段
(1)用戶自定義的Reducer要繼承自己的父類
(2)Reducer的輸入數據類型對應Mapper的輸出數據類型,也是KV
(3)Reducer的業務邏輯寫在reduce()方法中
(4)Reducetask進程對每一組相同k的<k,v>組調用一次reduce()方法
3)Driver階段
整個程序需要一個Drvier來進行提交,提交的是一個描述了各種必要信息的job對象
Hadoop序列化
爲什麼要序列化?
一般來說,“活的”對象只生存在內存裏,關機斷電就沒有了。而且“活的”對象只能由本地的進程使用,不能被髮送到網絡上的另外一臺計算機。 然而序列化可以存儲“活的”對象,可以將“活的”對象發送到遠程計算機。
什麼是序列化?
序列化就是把內存中的對象,轉換成字節序列(或其他數據傳輸協議)以便於存儲(持久化)和網絡傳輸。
反序列化就是將自己序列(或其他數據傳輸協議)或者是硬盤的持久化數據轉換爲內存的對象。
爲什麼不用Java的序列化?
Java的序列化是一個重量級序列化框架(Serializable),一個對象被序列化後,會附帶很多額外的信息(各種校驗信息,header,繼承體系等),不便於在網絡中高效傳輸。所以,hadoop自己開發了一套序列化機制(Writable),精簡、高效。
爲什麼序列化對Hadoop很重要?
因爲Hadoop在集羣之間進行通訊或者RPC調用的時候,需要序列化,而且要求序列化要快,且體積要小,佔用帶寬要小。所以必須理解Hadoop的序列化機制。
序列化和反序列化在分佈式數據處理領域經常出現:進程通信和永久存儲。
然而Hadoop中各個節點的通信是通過**遠程調用(RPC)**實現的,那麼RPC序列化要求具有以下特點:
1)緊湊:緊湊的格式能讓我們充分利用網絡帶寬,而帶寬是數據中心最稀缺的資
2)快速:進程通信形成了分佈式系統的骨架,所以需要儘量減少序列化和反序列化的性能開銷,這是基本的;
3)可擴展:協議爲了滿足新的需求變化,所以控制客戶端和服務器過程中,需要直接引進相應的協議,這些是新協議,原序列化方式能支持新的協議報文;
4)互操作:能支持不同語言寫的客戶端和服務端進行交互;
常用數據序列化類型
常用的數據類型對應的hadoop數據序列化類型
Java類型 | Hadoop Writable類型 |
---|---|
boolean | BooleanWritable |
byte | ByteWritable |
int | IntWritable |
float | FloatWritable |
long | LongWritable |
double | DoubleWritable |
string | Text |
map | MapWritable |
array | ArrayWritable |
也可以bean對象實現序列化接口(Writable),實現序列化
MapReduce工作流程
MapTask工作機制
- read階段:MapTask通過編寫的RecordReader,從輸入的InputSplit中解析出一個個key/value。
- map階段:該節點主要是將解析出的key/value交給編寫的map()函數處理,併產生一系列的key/value
- collect收集階段:在用戶編寫的map()函數中,當數據處理完成後,一般會調用OutPutCollect.collect()輸出結果。在該函數內部,會生成一個key/value分區(調用Partitioner),並寫入一個環形緩衝區中。
- spill階段:即“溢寫”,當環形緩衝區滿後,MapReduce會將數據寫到本地磁盤上,生成一個臨時文件。需要注意的是,將數據寫入本地磁盤之前,先要對數據進行一次本地排序,並在必要時對數據進行合併、壓縮等操作。
- Combine階段:當所有數據處理完成後,MapTask對所有臨時文件進行一次合併,以確保最終只會生成一個數據文件。
ReduceTask工作機制
- Copy階段:ReduceTask從各個MapTask上遠程拷貝一片數據,並針對某一片數據,如果其大小超過一定閾值,則寫到磁盤上,否則直接放到內存中。
- Merge階段:在遠程拷貝數據的同時,ReduceTask啓動了兩個後臺線程對內存和磁盤上的文件進行合併,以防止內存使用過多或磁盤上文件過多。
- Sort階段:按照MapReduce語義,用戶編寫reduce()函數輸入數據是按key進行聚集的一組數據。爲了將key相同的數據聚在一起,Hadoop採用了基於排序的策略。由於各個MapTask已經實現對自己的處理結果進行了局部排序,因此,ReduceTask只需對所有數據進行一次歸併排序即可。
- Reduce階段:reduce()函數將計算結果寫到HDFS上。
shuffle流程詳解
- maptask收集map方法輸出的kv對(key,value),放到內存緩衝區中。
- 從內存緩衝區中不斷溢出本地磁盤文件,可能會溢出多個文件
- 多個溢出文件會被合併成大的溢出文件
- 在溢出及合併過程中,都要調用partitioner進行分區和針對key進行排序。
- reducetask根據自己的分區號,去各個maptask機器上取得相應的結果分區數據。
- reducetask根據分區號,去各個maptask機器上相應的結果分區的數據。(如果reducetask只有一個,則所有分區都寫入那一個reducetask)
WordCount代碼(帶有Partitoner分區操作)
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>hdfs</groupId>
<artifactId>hdfs</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-hdfs</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.8.4</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.10</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
</dependency>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
WordcountDriver
package Wordcount;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.io.Text;
import overwrite.FileInputFormat;
import java.io.IOException;
/**
* @author wt
*/
public class WordcountDriver {
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
//args = new String [] {"D:\\data\\data\\","D:\\data\\wordcount"};
args = new String [] {"D:\\data\\web\\","D:\\data\\wordcount"};
//1.獲取配置信息
Configuration configuration = new Configuration();
Job job = Job.getInstance(configuration);
//設置加載路徑
job.setJarByClass(WordcountDriver.class);
//設置map和Reduce
job.setMapperClass(WordcountMapper.class);
job.setReducerClass(WordcountReducer.class);
//設置map輸出
job.setMapOutputKeyClass(Text.class);
job.setMapOutputValueClass(IntWritable.class);
//設置Reduce輸出
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(IntWritable.class);
//設置Partitioner分區
job.setPartitionerClass(WordcountPartitoner.class);
job.setNumReduceTasks(2);
// 判斷output文件夾是否存在,如果存在則刪除
Path path = new Path(args[1]);
FileSystem fileSystem = path.getFileSystem(configuration);
if (fileSystem.exists(path)) {
fileSystem.delete(path, true);
}
//設置輸入和輸出的路徑
FileInputFormat.setInputPaths(job, new Path(args[0]));
FileOutputFormat.setOutputPath(job, new Path(args[1]));
//提交
boolean result = job.waitForCompletion(true);
System.out.println(result?0:1);
}
}
WordcountMapper
package Wordcount;
import lombok.extern.slf4j.Slf4j;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Mapper;
import java.io.IOException;
/**
* @author wt
*/
@Slf4j
public class WordcountMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
/**
*
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void setup(Context context) throws IOException, InterruptedException {
try {
log.info("重寫setup"+context.getMapperClass().toString());
System.out.println("重寫setup"+context.getMapperClass().toString());
} catch (ClassNotFoundException e) {
log.error(""+e);
}
}
/**
*
* @param key
* @param value
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
//獲取數據
String line = new String(value.getBytes(),0,value.getLength(),"GBK");
//String line = value.toString();
//根據\t切割數據
//String[] words = line.split("\t");
String[] words = line.split("\\w+");
//根據空格切割
for(String word : words){
context.write(new Text(word),new IntWritable(1));
}
}
/**
*
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void cleanup(Context context) throws IOException, InterruptedException {
System.out.println("重寫cleanup");
}
}
WordcountReducer
package Wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Reducer;
import java.io.IOException;
/**
* @author wt
*/
public class WordcountReducer extends Reducer<Text, IntWritable, Text, IntWritable> {
/**
*
* @param key
* @param values
* @param context
* @throws IOException
* @throws InterruptedException
*/
@Override
protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
//累計求和
int sum = 0;
for (IntWritable conunt:values){
sum += conunt.get();
}
//輸出
context.write(key,new IntWritable(sum));
}
}
WordcountPartitoner(分區)
package Wordcount;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Partitioner;
/**
*
*/
public class WordcountPartitoner extends Partitioner<Text, IntWritable> {
/**
*
* @param text
* @param intWritable
* @param i
* @return
*/
@Override
public int getPartition(Text text, IntWritable intWritable, int i) {
//獲取單詞
String firWord="1";
if(text.toString().length() > 2){
firWord = text.toString().substring(0, 1);
}
int result = firWord.hashCode();
// 2 根據奇數偶數分區
if (result % 2 == 0) {
return 0;
}else {
return 1;
}
}
}
英語四六級
企業網:永久可查,四六級委員會終身備案。中國500強企業、銀行、國企等在四六級委員會都有對接,有後臺會員號,可以很容易調出學生檔案。原版真紙,三大防僞,可過機器。