MapReduce分佈式離線計算框架學習摘要(一)

一、MapReduce簡單概述
在Hadoop中有兩個核心的模塊,一個是大數據量文件的存儲HDFS,另一個是能夠做快速的數據分析,則爲MapReduce。
百度百科介紹:
二、MapReduce的特點
它適合做“離線”(存儲在本地)的海量數據計算,通常計算的數據量在PB級別或者ZB級別
MapReduce的主要特點如下:
  1. 易於編程
  2. 擴展性良好
  3. 高容錯性
 
三、MapReduce的應用場景
    MapReduce對於離線的大數據量分析計算還是需要一定時間的,不能實時做計算分析,它的典型應用有以下一些:
  1. 簡單的數據統計,比如網站的pv、uv
  2. 搜索引擎建立索引
  3. 在搜索引擎中統計最流行的搜索詞
  4. 統計搜索的詞頻率
  5. 複雜的數據分析算法實現
    MapReduce也有不適用的的場景
        1. 實時計算
        2. 流式計算(MapReduce對於數據集要求是靜態的,不能是動態的)
  1. DAG計算,當多個應用程序存在依賴關係,且後一個應用的輸入來自於前一個輸出這個情況是不適合用MapReduce的
 
四、MapReduce是如何運行的(通過實例來理解這個過程)
        單詞統計,分析的過程如下圖所示
        
        圖中主要分爲五個過程
            Split:把大文件進行切割成多份
            Map:分別針對切出來的小文件,解析出每一個字符並在後面記上數字1
            Shuffle:每一份中的字符進行分組到一起並做統計
            Reduce:把對應的字符進行累加
            Final result:輸出最終的結果
    
        上面我們總結出了大致的執行過程,接下來看看具體的執行過程如何
            1. 把數據切割成數據片段
            2. 數據片段以key和value的形式被讀進來處理,默認情況是以行的下標作爲key,行的內容做爲value
            3. 數據傳入Map中進行處理(處理邏輯由用戶定義),在Map中處理完成後還是以key和value的形式輸出
            4. 輸出的數據給到Shuffle,它進行數據的排序合併等操作,但是它不會修改傳入的數據,這個時候還是key2和value
            5. 數據接下來傳給Reduce進行處理,它處理完後,生成key3和value3
            6. Reduce處理完的數據會被寫到HDFS的某個目錄中
        接下來針對 Split、Map、Reduce及Shuffle做分別的簡單介紹
        1. Split(MapReduce的文件切片)
            第一個問題,它按多大的大小進行切片?
            默認與block對應,這個大小也可以由用戶自行控制,MapReduce的Split大小計算公式如下:max(min.split, min(max.split, block))
            max.split = totalSize/numSplit(totalSize:文件大小;numSplit:用戶設定的map task的個數,默認爲1)
            min.split = InputSplit的最小值(可以在配置文件中進行配置參數,mapred.min.split.size,在不配置時默認爲1B,block是HDFS中塊的大小)
 
        2. Map過程與Reduce過程
            Map和Reduce的實現邏輯都是由程序員完成的.
            Map個數與Split的個數對應起來,一個Split切片對應一個Map任務
            Reduce的默認數是1,這個是可以自行設置的。
            注意:一個程序可能只有一個Map任務卻沒有Reduce任務,也可能是多個MapReduce程序串連起來,比如,第一個MapReduce的輸出結果作爲第二個MapReduce的輸入,第二個MapReduce的輸出又是第三個MapReduce的輸入,最終所有的MapReduce完成才完成一個任務。
 
        3. Shuffle過程
            它又叫“洗牌”,它起到了連接Map任務與Reduce任務的作用。
            Shuffle分爲兩個部分,一部分在Map端,另一部分在Reduce端
            Map處理後的數據會以key、value的形式存在緩衝區中,這個緩衝區大小是128MB,當這個緩衝區快要溢出時(默認爲80%),會把數據寫到磁盤中生成文件,這個過程叫做溢寫操作。
            溢寫磁盤的工作由一個線程來完成,溢寫之前包括Partition(分區)、Sort(排序)都有默認的實現
            Partition:默認按hash值%reduce數量進行分區
            Sort:默認按字符順序進行排序
            在完成溢寫後,在磁盤上會生成多個文件,多個文件會通過merge線程完成文件合併
            合併完成後的數據(以key和value的形式存在),會基於Partition被髮送到不同的Reduce上
 
五、MapReduce實例
 
實例一:對文件中的單詞進行統計,字符之間使用空格進行分隔
相關的JAR包
    hadoop-3.1.3\share\hadoop\common 下的jar及hadoop-3.1.3\share\hadoop\common\lib下的jar包
    hadoop-3.1.3\share\hadoop\mapreduce下的jar及hadoop-3.1.3\share\hadoop\mapreduce\lib下的jar包
    hadoop-3.1.3\share\hadoop\hdfs下及hadoop-3.1.3\share\hadoop\hdfs\lib下的jar包
    一個完整的MapReduce分爲兩個部分:一個是Mapper;另一個是Reducer
    
    Mapper
    它是用來對文件進行切割並且循環輸出給到Reducer
    
public class WordCountMapper extends Mapper<LongWritable,Text,Text,IntWritable>{
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //使用空隔進行分詞
        String[] str = value.toString().split(" ");
        //for循環處理
        for(int i = 0; i < str.length; i++){
            //new Text,new IntWritable進行可序列化
            context.write(new Text(str[i]),new IntWritable(1));
        }
    }
}

 

    
    Readucer
    它將從Map轉入的詞彙進行分組合並,並通過文本和單詞統計量的方式輸出
public class WordCountReducer extends Reducer<Text,IntWritable,Text,IntWritable> {
    @Override
    protected void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException {
        //數據進行分組合並輸出
        int sum = 0;
        for(IntWritable i:values){
            sum = sum+i.get();
        }
        context.write(key,new IntWritable(sum));
    }
}

 

 
    主方法所在類
    主方法中用來連接HDFS、要讀取的文件及處理後的文件在HDFS中的路徑。同時需要指明我們所要進行Map和Reduce過程的類
public class MRRunJob {
    public static void main(String[] args) {
        Configuration conf = new Configuration();
        //NameNode入口
        conf.set("fs.defaultFS","hdfs://192.168.2.4:8020");
        Job job = null;
        try {
            job = Job.getInstance(conf,"mywc");
        } catch (IOException e) {
            e.printStackTrace();
        }


        //主類
        job.setJarByClass(MRRunJob.class);
        //Mapper類
        job.setMapperClass(WordCountMapper.class);
        //Reducer類
        job.setReducerClass(WordCountReducer.class);
        //Map輸出的value類型
        job.setOutputKeyClass(Text.class);
        //Map輸出的value類型
        job.setOutputValueClass(IntWritable.class);


        try {
            //讀取文件位置
            job.setWorkingDirectory(new Path("/"));
            System.out.println(job.getWorkingDirectory());
            FileInputFormat.addInputPath(job,new Path("/usr/input/data/wc/"));
            //處理完成後數據存儲的位置(注意:如果輸出文件夾存在則會報錯)
            FileOutputFormat.setOutputPath(job,new Path("/usr/output/data/wc/"));
            job.waitForCompletion(true);
        } catch (Exception e) {
            e.printStackTrace();
        }


    }
}

 

    注意:上面的類一定是要在HDFS中的路徑是否則程序會報錯提示找不到文件位置。切記!!
 
實例二:數據清洗ETL實例(對時間進行格式化處理)
Mapper
public class ETLMapper extends Mapper<LongWritable,Text,Text,NullWritable> {
    @Override
    protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
        //使用,號對文本內容進行分隔
        String[] strArray = value.toString().split(",");
        String strContent = "";


        for(int i=0;i<strArray.length;i++){
            //把下標爲第三個數據轉換爲yyyy-MM-dd HH:mm:ss的時間格式
            if(i == 3){
                SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                //把原數據的秒轉爲毫秒
                String str = sdf.format(Long.parseLong(strArray[i]+"000"));
                strContent = strContent + str + ",";
            } else {
                strContent = strContent + strArray[i] + ",";
            }
        }
        context.write(new Text(strContent),NullWritable.get());
    }
}

 

Reducer
public class ETLReducer extends Reducer<Text,NullWritable,NullWritable,Text> {
    @Override
    protected void reduce(Text key, Iterable<NullWritable> values, Context context) throws IOException, InterruptedException {
        //把Mapper傳過來的內容寫入磁盤
        context.write(NullWritable.get(),key);
    }
}

 

主方法
public class MRRunJob {
    public static void main(String[] args) {
        Configuration conf = new Configuration();
        conf.set("fs.defaultFS","hdfs://192.168.2.4:8020");
        //初始化fs
        FileSystem fs = null;
        try {
            fs = FileSystem.get(conf);
        } catch (IOException e) {
            e.printStackTrace();
        }
        Job job = null;
        try {
            job = Job.getInstance(conf,"mywc");
        } catch (IOException e) {
            e.printStackTrace();
        }
        //主方法
        job.setJarByClass(MRRunJob.class);
        //Mapper方法
        job.setMapperClass(ETLMapper.class);
        //Reducer方法
        job.setReducerClass(ETLReducer.class);
        //Map輸出的key類型
        job.setOutputKeyClass(Text.class);
        //Map輸出的value類型
        job.setOutputValueClass(NullWritable.class);


        try {
            //讀取文件位置
            Path inputPath = new Path("/usr/input/data/etl/");
            //如果這個文件不存在則新增
            if(!fs.exists(inputPath)){
                fs.mkdirs(inputPath);
            }
            //需要把本地的文件上傳到HDFS
            Path src = new Path("D:\\IdeaProjects\\MapReduceTest\\ETLDemo\\etl01.txt");
            fs.copyFromLocalFile(src,inputPath);


            FileInputFormat.addInputPath(job,inputPath);


            //處理完成後文件輸出位置
            Path outputPath = new Path("/usr/output/data/etl01/");
            //如果這個輸出的目錄存在則刪除
            if(fs.exists(outputPath)){
                fs.delete(outputPath,true);
            }
            FileOutputFormat.setOutputPath(job,outputPath);
            boolean f = job.waitForCompletion(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

 

注意:如果在window環境中需要運行成功的話,需要修改原碼 NativeIO.java
把這個源碼複製到自己的工程當中,並修改如下內容:
把原來的return註釋掉,直接reurn true即可
如果不做這個操作,則在運行時會報錯:org.apache.hadoop.io.nativeio.NativeIO$Windows.access0(Ljava/lang/String;I)Z
 
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章