一、說明:該實驗環境是基於虛擬機Ubuntu、hadoop、eclipse、mapreduce。
1、MapReduce模型簡介:
•MapReduce將複雜的、運行於大規模集羣上的並行計算過程高度地抽象到了兩個函數:Map(Map 任務 (分割及映射))和Reduce(Reduce 任務 (重排,還原))
•編程容易,不需要掌握分佈式並行編程細節,也可以很容易把自己的程序運行在分佈式系統上,完成海量數據的計算
•MapReduce採用“分而治之”策略,一個存儲在分佈式文件系統中的大規模數據集,會被切分成許多獨立的分片(split),這些分片可以被多個Map任務並行處理
•MapReduce設計的一個理念就是“計算向數據靠攏”,而不是“數據向計算靠攏”,因爲,移動數據需要大量的網絡傳輸開銷
•MapReduce框架採用了Master/Slave架構,包括一個Master和若干個Slave。Master上運行JobTracker,Slave上運行TaskTracker
•Hadoop框架是用Java實現的,但是,MapReduce應用程序則不一定要用Java來寫
2、詳細的整個過程
•映射的任務是爲每個分割創建在分割每條記錄執行映射的函數。
•有多個分割是好處的, 因爲處理一個分割使用的時間相比整個輸入的處理的時間要少, 當分割比較小時,處理負載平衡是比較好的,因爲我們正在並行地處理分割。
•然而,也不希望分割的規模太小。當分割太小,管理分割和映射創建任務的超負荷開始逐步控制總的作業執行時間。
•對於大多數作業,最好是分割成大小等於一個HDFS塊的大小(這是64 MB,默認情況下)。
•map任務執行結果到輸出寫入到本地磁盤的各個節點上,而不是HDFS。
•之所以選擇本地磁盤而不是HDFS是因爲,避免複製其中發生 HDFS 存儲操作。
•映射輸出是由減少任務處理以產生最終的輸出中間輸出。
•一旦任務完成,映射輸出可以扔掉了。所以,複製並將其存儲在HDFS變得大材小用。
•在節點故障的映射輸出之前,由 reduce 任務消耗,Hadoop 重新運行另一個節點在映射上的任務,並重新創建的映射輸出。
•減少任務不會在數據局部性的概念上工作。每個map任務的輸出被供給到 reduce 任務。映射輸出被傳輸至計算機,其中 reduce 任務正在運行。
•在此機器輸出合併,然後傳遞到用戶定義的 reduce 函數。
•不像到映射輸出,reduce輸出存儲在HDFS(第一個副本被存儲在本地節點上,其他副本被存儲於偏離機架的節點)。因此,寫入 reduce 輸出
二、實際運用(股票統計)
1、需要處理的股票數據文件夾內容(export)
2、其中一個文件部分內容
3、編碼解析:
(1)map函數分割:
public static class Map extends Mapper<Object,Text,Text,Text>{
private Text text = new Text();
private Text keys = new Text();
private int no = 0;
public void map(Object key,Text value,Context context)throws IOException,InterruptedException{
String line = value.toString();
this.no +=1;
System.out.println(this.no+line);
String[] lines = line.split("\\s+");
for(int i =0;i<lines.length;i++){
System.out.print(lines[i]+" ~~");
}
if(this.no == 1){
this.keys.set("股票編碼:"+lines[0]);
}
if(this.no > 2){
if(lines.length == 7){
this.text.set(lines[0]+"+"+lines[1]+"+"+lines[4]);
System.out.println(this.no+"---->"+lines[0]+"+"+lines[1]+"+"+lines[4]);
context.write(this.keys, this.text);
}
}
}
}
(2)reduce函數還原:
public static class Reduce extends Reducer<Text,Text,Text,Text>{
private Text text = new Text();
public void reduce(Text key,Iterable<Text> values,Context context) throws IOException, InterruptedException{
double sum1 = 0.0;
double sum2 = 0.0;
int n = 0;
System.out.println("...................start"+key.toString());
Iterator<Text> $it = values.iterator();
while($it.hasNext()){
String record =$it.next().toString();
System.out.println(n);
System.out.println("原始數據:"+record);
n++;
System.out.println("第"+n+"次循環");
String []result = record.split("[+]");
System.out.println(Double.valueOf(result[1])+" "+Double.valueOf(result[2]));
sum1 +=(Double.valueOf(result[1])*100);
sum2 +=(Double.valueOf(result[2])*100);
System.out.println(sum1/100+" "+sum2/100);
}
System.out.println("最後的結果:"+sum1/100+" "+sum2/100);
double openPrise = sum1/(100*n);
double closePrise = sum2/(100*n);
openPrise = (double)Math.round(openPrise*100)/100;
closePrise = (double)Math.round(closePrise*100)/100;
System.out.println("平均值:"+openPrise+" "+closePrise);
Double.toString(closePrise);
String result ="開盤平均價:"+Double.toString(openPrise)+", 收盤平均價:"+Double.toString(closePrise);
this.text.set(result);
context.write(key, this.text);
}
}
4、部分結果顯示
5、完整代碼:
import java.io.IOException;
import java.util.Iterator;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.Text;
import org.apache.hadoop.mapreduce.Job;
import org.apache.hadoop.mapreduce.Mapper;
import org.apache.hadoop.mapreduce.Reducer;
import org.apache.hadoop.mapreduce.lib.input.FileInputFormat;
import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat;
import org.apache.hadoop.util.GenericOptionsParser;
public class Data{
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Configuration conf = new Configuration();
conf.set("fs.default.name", "hdfs://localhost:9000");
String[] otherArgs = (new GenericOptionsParser(conf,args)).getRemainingArgs();
if(otherArgs.length<2){
System.err.println("Usage:Data<in><out>");
System.exit(2);
}
Job job = Job.getInstance(conf,"Data");
job.setJarByClass(Data.class);
job.setMapperClass(Data.Map.class);
System.out.println("Mapper over");
job.setReducerClass(Data.Reduce.class);
System.out.println("Reduce over");
job.setOutputKeyClass(Text.class);
job.setOutputValueClass(Text.class);
System.out.println("all over");
for(int i = 0;i<otherArgs.length-1;i++){
FileInputFormat.addInputPath(job, new Path(otherArgs[i]));
}
FileOutputFormat.setOutputPath(job, new Path(otherArgs[otherArgs.length-1]));
System.exit(job.waitForCompletion(true)?0:1);
}
public static class Map extends Mapper<Object,Text,Text,Text>{
private Text text = new Text();
private Text keys = new Text();
private int no = 0;
public void map(Object key,Text value,Context context)throws IOException,InterruptedException{
String line = value.toString();
this.no +=1;
System.out.println(this.no+line);
String[] lines = line.split("\\s+");
for(int i =0;i<lines.length;i++){
System.out.print(lines[i]+" ~~");
}
if(this.no == 1){
this.keys.set("股票編碼:"+lines[0]);
}
if(this.no > 2){
if(lines.length == 7){
this.text.set(lines[0]+"+"+lines[1]+"+"+lines[4]);
System.out.println(this.no+"---->"+lines[0]+"+"+lines[1]+"+"+lines[4]);
context.write(this.keys, this.text);
}
}
}
}
public static class Reduce extends Reducer<Text,Text,Text,Text>{
private Text text = new Text();
public void reduce(Text key,Iterable<Text> values,Context context) throws IOException, InterruptedException{
double sum1 = 0.0;
double sum2 = 0.0;
int n = 0;
System.out.println("...................start"+key.toString());
Iterator<Text> $it = values.iterator();
while($it.hasNext()){
String record =$it.next().toString();
System.out.println(n);
System.out.println("原始數據:"+record);
n++;
System.out.println("第"+n+"次循環");
String []result = record.split("[+]");
System.out.println(Double.valueOf(result[1])+" "+Double.valueOf(result[2]));
sum1 +=(Double.valueOf(result[1])*100);
sum2 +=(Double.valueOf(result[2])*100);
System.out.println(sum1/100+" "+sum2/100);
}
System.out.println("最後的結果:"+sum1/100+" "+sum2/100);
double openPrise = sum1/(100*n);
double closePrise = sum2/(100*n);
openPrise = (double)Math.round(openPrise*100)/100;
closePrise = (double)Math.round(closePrise*100)/100;
System.out.println("平均值:"+openPrise+" "+closePrise);
Double.toString(closePrise);
String result ="開盤平均價:"+Double.toString(openPrise)+", 收盤平均價:"+Double.toString(closePrise);
this.text.set(result);
context.write(key, this.text);
}
}
}