hadoop學習(六)WordCount樣本深度學習MapReduce過程(1)

來源:互聯網
上載者:User

        花了整整一個下午(6個多小時),整理總結,也算是對這方面有一個深度的瞭解。日後可以回頭多看看。

        我們都安裝完Hadoop之後,按照一些案例先要跑一個WourdCount程式,來測試Hadoop安裝是否成功。在終端中用命令建立一個檔案夾,簡單的向兩個檔案中各寫入一段話,然後運行Hadoop,WourdCount內建WourdCount程式指令,就可以輸出寫入的那句話各個不同單詞的個數。但是這不是這篇部落客要講的內容,主要是想通過一個簡單的Wordcount程式,來認識Hadoop的內部機制。並通過此來深入瞭解MapReduce的詳細過程。在Thinking in BigDate(八)大資料Hadoop核心架構HDFS+MapReduce+Hbase+Hive內部機理詳解中我們已經很大概梳理一下,Hadoop內部叢集架構,並對MapReduce也有初步的瞭解,這裡我們以WourdCount程式來深入的探討MapReduce的過程。

利用命令列,測試WourdCount程式:

WourdCount程式就是統計文本中字母的個數

1、建立Wordcount樣本檔案
zhangzhen@ubuntu:~/software$ mkdir inputzhangzhen@ubuntu:~/software$ cd input/zhangzhen@ubuntu:~/software/input$ echo "I am zhangzhen">test1.txtzhangzhen@ubuntu:~/software/input$ echo "You are not zhangzhen">test2.txtzhangzhen@ubuntu:~/software/input$ cd ../hadoop-1.2.1/zhangzhen@ubuntu:~/software/hadoop-1.2.1$ cd binzhangzhen@ubuntu:~/software/hadoop-1.2.1/bin$ lshadoop             slaves.sh                  start-mapred.sh           stop-mapred.shhadoop-config.sh   start-all.sh               stop-all.sh               task-controllerhadoop-daemon.sh   start-balancer.sh          stop-balancer.shhadoop-daemons.sh  start-dfs.sh               stop-dfs.shrcc                start-jobhistoryserver.sh  stop-jobhistoryserver.shzhangzhen@ubuntu:~/software/hadoop-1.2.1/bin$ jps(確定Hadoop已經起來了)7101 SecondaryNameNode7193 JobTracker7397 TaskTracker9573 Jps6871 DataNode6667 NameNodezhangzhen@ubuntu:~/software/hadoop-1.2.1/bin$ cd ..zhangzhen@ubuntu:~/software/hadoop-1.2.1$ lsbin          data                       hadoop-minicluster-1.2.1.jar  libexec      sharebuild.xml    docs                       hadoop-test-1.2.1.jar         LICENSE.txt  srcc++          hadoop-ant-1.2.1.jar       hadoop-tools-1.2.1.jar        logs         webappsCHANGES.txt  hadoop-client-1.2.1.jar    ivy                           NOTICE.txtconf         hadoop-core-1.2.1.jar      ivy.xml                       README.txtcontrib      hadoop-examples-1.2.1.jar  lib                           sbinzhangzhen@ubuntu:~/software/hadoop-1.2.1$ bin/hadoop dfs -put ../input in  //把檔案上傳的hdfa中的in目錄中,其實這個說法有誤zhangzhen@ubuntu:~/software/hadoop-1.2.1$ bin/hadoop dfs -ls .in/*ls: Cannot access .in/*: No such file or directory.zhangzhen@ubuntu:~/software/hadoop-1.2.1$ bin/hadoop dfs -ls ./in/*-rw-r--r--   1 zhangzhen supergroup         15 2014-03-22 10:45 /user/zhangzhen/in/test1.txt-rw-r--r--   1 zhangzhen supergroup         22 2014-03-22 10:45 /user/zhangzhen/in/test2.txt 

        注意:Hadoop中是沒有目前的目錄這個概念的。所以上傳到hdfs中的檔案,我們是不能通過cd命令、ls命令,查看目錄中的檔案。這裡我們通過就是上面和下面命令查看hdfs中檔案的方法。

       在每個版本中,hadoop-examples-1.2.1.jar的位置不一樣,在Hadoop1.2.1版本中,我們hadoop-examples-1.2.1.jar檔案是在Hadoop目錄中的,這裡我們需要把這個hadoop-examples-1.2.1.jar拷貝到/bin 目錄中。

       執行:利用hadoop-examples-1.2.1.jar執行bin目錄下in目錄中的檔案,並把結果寫入到 put 的檔案夾。

zhangzhen@ubuntu:~/software$ bin/hadoop jar hadoop-examples-1.2.1.jar wordcount in put

查看輸出的結果:

zhangzhen@ubuntu:~/software/hadoop-1.2.1$ bin/hadoop dfs -lsFound 2 itemsdrwxr-xr-x   - zhangzhen supergroup          0 2014-03-22 10:45 /user/zhangzhen/indrwxr-xr-x   - zhangzhen supergroup          0 2014-03-22 10:56 /user/zhangzhen/putzhangzhen@ubuntu:~/software/hadoop-1.2.1$ bin/hadoop dfs -ls ./putFound 3 items-rw-r--r--   1 zhangzhen supergroup          0 2014-03-22 10:56 /user/zhangzhen/put/_SUCCESSdrwxr-xr-x   - zhangzhen supergroup          0 2014-03-22 10:56 /user/zhangzhen/put/_logs  目錄-rw-r--r--   1 zhangzhen supergroup         39 2014-03-22 10:56 /user/zhangzhen/put/part-r-00000   這是檔案zhangzhen@ubuntu:~/software/hadoop-1.2.1/hadoop dfs -cat ./put/*I      1You    1am     1are    1not    1zhangzhen    2cat: File does not exist: /user/zhangzhen/put/_logszhangzhen@ubuntu:~/software/hadoop-1.2.1$ 

         上面的結果,就基本可以證明Hadoop搭建是沒有問題的。執行hadoop-examples-1.2.1.jar程式,其實是把java程式編譯打成一個jar檔案,然後直接運行,就可以得到結果。其實這也是以後我們運行java程式的一個方法。把程式編譯打包上傳,然後運行。還有另一種方面,eclipse串連Hadoop,可以聯機測試。兩種方法各有優點,不再詳述。

       啟動並執行程式,我們可以在Hadoop的安裝目錄中找到源檔案,WourdCount.java原始碼。

zhangzhen@ubuntu:~/software/hadoop-1.2.1/src/examples/org/apache/hadoop/examples$ pwd /home/zhangzhen/software/hadoop-1.2.1/src/examples/org/apache/hadoop/examples zhangzhen@ubuntu:~/software/hadoop-1.2.1/src/examples/org/apache/hadoop/examples$ 

      下面是把原始碼拷到eclipse程式中,利用此代碼(並未修改)測試一下實際的資料並得到結果。(注釋是對上以一行的解釋)

import java.io.IOException;  import java.util.StringTokenizer;    import org.apache.hadoop.conf.Configuration;  import org.apache.hadoop.fs.Path;  import org.apache.hadoop.io.IntWritable;  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 Wordcount {      public static class TokenizerMapper          extends Mapper<Object, Text, Text, IntWritable>{ //規定map中用到的資料類型,這裡的Text相當於jdk中的String IntWritable相當於jdk的int類型,//這樣做的原因主要是為了hadoop的資料序化而做的。           private final static IntWritable one = new IntWritable(1);//聲時一個IntWritable變數,作計數用,每出現一個key,給其一個value=1的值      private Text word = new Text();//用來暫存map輸出中的key值,Text類型的              public void map(Object key, Text value, Context context                      ) throws IOException, InterruptedException { //這就是map函數,它是和Mapper抽象類別中的相對應的,此處的Object key,Text value的類型和上邊的Object,//Text是相對應的,而且最好一樣,不然的話,多數情況運行時會報錯。      StringTokenizer itr = new StringTokenizer(value.toString());//Hadoop讀入的value是以行為單位的,其key為該行所對應的行號,因為我們要計算每個單詞的數目,//預設以空格作為間隔,故用StringTokenizer輔助做字串的拆分,也可以用string.split("")來作。      while (itr.hasMoreTokens()) { //遍曆一下每行字串中的單詞         word.set(itr.nextToken());  //出現一個單詞就給它設成一個key並將其值設為1        context.write(word, one);   //輸出設成的key/value值//上面就是map打散的過程      }      }    }        public static class IntSumReducer          extends Reducer<Text,IntWritable,Text,IntWritable> {//reduce的靜態類,這裡和Map中的作用是一樣的,設定輸入/輸出的值的類型    private IntWritable result = new IntWritable();        public void reduce(Text key, Iterable<IntWritable> values,                          Context context                         ) throws IOException, InterruptedException {        int sum = 0;        for (IntWritable val : values) {  //由於map的打散,這裡會得到如,{key,values}={"hello",{1,1,....}},這樣的集合        sum += val.get();               //這裡需要逐一將它們的value取出來予以相加,取得總的出現次數,即為匯和      }        result.set(sum);                  //將values的和取得,並設成result對應的值      context.write(key, result);//此時的key即為map打散之後輸出的key,沒有變化,變化的時result,以前得到的是一個數位集合,//已經給算出和了,並做為key/value輸出。      }    }      public static void main(String[] args) throws Exception {      Configuration conf = new Configuration();  //取得系統的參數    String[] otherArgs = new GenericOptionsParser(conf, args).getRemainingArgs();      if (otherArgs.length != 2) {                //判斷一下命令列輸入路徑/輸出路徑是否齊全,即是否為兩個參數      System.err.println("Usage: wordcount <in> <out>");        System.exit(2);                           //若非兩個參數,即退出    }      Job job = new Job(conf, "word count");      //此程式的執行,在hadoop看來是一個Job,故進行初始化job操作    job.setJarByClass(Wordcount.class);         //可以認為成,此程式要執行MyWordCount.class這個位元組碼檔案    job.setMapperClass(TokenizerMapper.class); //在這個job中,我用TokenizerMapper這個類的map函數    job.setCombinerClass(IntSumReducer.class);      job.setReducerClass(IntSumReducer.class);   //在這個job中,我用IntSumReducer這個類的reduce函數     job.setOutputKeyClass(Text.class);          //在reduce的輸出時,key的輸出類型為Text    job.setOutputValueClass(IntWritable.class);  //在reduce的輸出時,value的輸出類型為IntWritable    FileInputFormat.addInputPath(job, new Path(otherArgs[0]));  //初始化要計算word的檔案的路徑    FileOutputFormat.setOutputPath(job, new Path(otherArgs[1])); //初始化要計算word的檔案的之後的結果的輸出路徑     System.exit(job.waitForCompletion(true) ? 0 : 1); //提交job到hadoop上去執行了,意思是指如果這個job真正的執行完了則主函數退出了,若沒有真正的執行完就退出了。    } //參考:http://hi.baidu.com/erliang20088/item/ce550f2f088ff1ce0e37f930}

WourdCount程式中隱藏的秘密

1、具體流程:

        1)檔案拆分成splits,由於測試用的檔案較小,所以每個檔案為一個split,並將檔案按行分割形成<key,value> 對,如。這一步由MapReduce架構自動完成,其中位移量(即key值)包括了斷行符號所佔的字元數和Linux環境有關。


       2)將分割好的<key,value>對交給使用者定義的map方法進行處理,產生新的<key,value>對。


       3)得到map方法輸出的<key,value>對後,Mapper會將它們按照key值進行排序,並執行Combine過程,將key至相同value值累加,得到Mapper的最終輸出結果。



2、Map Task的整體流程:

可以概括為5個步驟:

         1)Read:Map Task通過使用者編寫的RecordReader,從輸入InputSplit中解析出一個個key/value。

         2)Map:該階段主要將解析出的key/value交給使用者編寫的map()函數處理,併產生一系列的key/value。

         3)Collect:在使用者編寫的map()函數中,當資料處理完成後,一般會調用OutputCollector.collect()輸入結果。在該函數內部,它會將產生的key/value分區(通過Partitioner),並寫入一個環形記憶體緩衝區中。

        4)Spill:即“溢寫”,當環形緩衝區滿後,MapReduce會將資料寫到本地磁碟上,產生一個臨時檔案。將資料寫入本地磁碟之前,先要對資料進行一次本地排序,並在必要時對資料進行合并,壓縮等操作。

        5)Combine:當所有資料處理完成後,Map Task對所有臨時檔案進行一次合并,以確保最終只會產生一個資料檔案。

3、Reduce的整體流程:

可以概括為5個步驟:

        1)Shuffle:也稱Copy階段。Reduce Task從各個Map Task上遠程拷貝一片資料,並針對某一片資料,如果其大小超過一定閥值,則寫到磁碟上,否則直接放到記憶體中。

        2)Merge:在遠程拷貝的同時,Reduce Task啟動了兩個後台線程對記憶體和磁碟上的檔案進行合并,以防止記憶體使用量過多或者磁碟上檔案過多。

        3)Sort:按照MapReduce語義,使用者編寫的reduce()函數輸入資料是按key進行聚集的一組資料。為了將key相同的資料聚在一 起,Hadoop採用了基於排序的策略。由於各個Map Task已經實現了對自己的處理結果進行了局部排序,因此,Reduce Task只需對所有資料進行一次歸併排序即可。

        4)Reduce:在該階段中,Reduce Task將每組資料依次交給使用者編寫的reduce()函數處理。

        5)Write:reduce()函數將計算結果寫到HDFS。

        通過一些部落格對WourdCount的介紹樣本,總結Map、Reduce的整個過程。加上Thinking in BigDate(八)大資料Hadoop核心架構HDFS+MapReduce+Hbase+Hive內部機理詳解所將的內容,大致把整個檔案資料處理的過程梳理一遍。但是還有很多細節沒有講明。如:Spill、Combine、Shuffle的過程,Shuffle整個MapReduce的核心。接下來,我們更深入瞭解MapReduce的過程,更深入的瞭解,便於我們在以後在操作Hadoop叢集的過程中,有利於系統調優,甚至修改Hadoop原始碼。


 

CopyrightBUAA


相關文章

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.