離線計算三 MAPREDUCE詳解

目錄
課程大綱(MAPREDUCE詳解) 3

  1. MAPREDUCE入門 4
    1.1 爲什麼要MAPREDUCE 4
    1.2 MAPREDUCE程序運行演示 4
    1.3 MAPREDUCE 示例編寫及編程規範 4
    1.3.1 編程規範 4
    1.3.2 wordcount示例編寫 5
    1.4 MAPREDUCE程序運行模式及debug方法 7
    1.4.1 本地運行模式 7
    1.4.2 集羣運行模式 7
  2. Mapreduce程序的核心運行機制 8
    2.1 概述 8
    2.2 mr程序運行流程 8
    2.2.1 流程示意圖 8
    2.2.2 流程解析 8
    2.3 Maptask實例數的決定機制 10
    2.3.1 maptask數量的決定機制 10
    2.3.2切片機制: 10
    2.4 ReduceTask實例數的決定 11
  3. MAPREDUCE中的Combiner 12
  4. MAPREDUCE中的序列化 12
    4.1 概述 12
    4.2 Jdk序列化和MR序列化之間的比較 12
    4.3 自定義對象實現MR中的序列化接口 13
  5. Mapreduce中的排序初步 16
    5.1 需求: 16
    5.2 分析 16
    5.3 實現 16
  6. Mapreduce中的分區Partitioner 20
    6.1 需求: 20
    6.2 分析 20
    6.3 實現 20
  7. mapreduce的shuffle機制 22
    7.1 概述: 22
    7.2 主要流程: 22
    7.3 詳細流程 22
    7.4 詳細流程示意圖 23
  8. mapreduce數據壓縮 24
    8.1 概述 24
    8.2 MR支持的壓縮編碼 24
    8.3 Reducer輸出壓縮 24
    8.4 Mapper輸出壓縮 25
    8.5 壓縮文件的讀取 25
  9. MapReduce與YARN 27
    9.1 YARN概述 27
    9.2 YARN的重要概念 27
    9.3 Yarn中運行運算程序的示例 27
  10. MapReduce編程案例 28
    10.1 reduce端join算法實現 28
    10.2 map端join算法實現 29
    10.3 web日誌預處理 32
    附:Mapreduce參數優化 36
    11.1 資源相關參數 36
    11.2 容錯相關參數 37
    11.3 本地運行mapreduce 作業 37
    11.4 效率和穩定性相關參數 37

課程大綱(MAPREDUCE詳解)
MapReduce快速入門 如何理解map、reduce計算模型
Mapreudce程序運行演示
Mapreduce編程規範及示例編寫
Mapreduce程序運行模式及debug方法
MapReduce高級特性 Mapreduce程序的核心機制
MapReduce的序列化框架
MapReduce的排序實現
MapReduce的分區機制及自定義
Mapreduce的數據壓縮
Mapreduce與yarn的結合
Mapreduce編程案例
Mapreduce 參數優化

目標:
掌握mapreduce分佈式運算框架的編程思想
掌握mapreduce常用算法的編程套路
掌握mapreduce分佈式運算框架的運行機制,具備一定自定義開發的能力

  1. MAPREDUCE原理篇(1)
    Mapreduce是一個分佈式運算程序的編程框架,是用戶開發“基於hadoop的數據分析應用”的核心框架;
    Mapreduce核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成一個完整的分佈式運算程序,併發運行在一個hadoop集羣上;

1.1 爲什麼要MAPREDUCE
(1)海量數據在單機上處理因爲硬件資源限制,無法勝任
(2)而一旦將單機版程序擴展到集羣來分佈式運行,將極大增加程序的複雜度和開發難度
(3)引入mapreduce框架後,開發人員可以將絕大部分工作集中在業務邏輯的開發上,而將分佈式計算中的複雜性交由框架來處理

設想一個海量數據場景下的wordcount需求:
單機版:內存受限,磁盤受限,運算能力受限分佈式:文件分佈式存儲(HDFS)運算邏輯需要至少分成2個階段(一個階段獨立併發,一個階段匯聚)運算程序如何分發程序如何分配運算任務(切片)兩階段的程序如何啓動?如何協調?整個程序運行過程中的監控?容錯?重試?

可見在程序由單機版擴成分佈式時,會引入大量的複雜工作。爲了提高開發效率,可以將分佈式程序中的公共功能封裝成框架,讓開發人員可以將精力集中於業務邏輯。

而mapreduce就是這樣一個分佈式程序的通用框架,其應對以上問題的整體結構如下:
MRAppMaster(mapreduce application master)MapTaskReduceTask

1.2 MAPREDUCE框架結構及核心運行機制
1.2.1 結構
一個完整的mapreduce程序在分佈式運行時有三類實例進程:
1、MRAppMaster:負責整個程序的過程調度及狀態協調
2、mapTask:負責map階段的整個數據處理流程
3、ReduceTask:負責reduce階段的整個數據處理流程

1.2.2 MR程序運行流程
1.2.2.1 流程示意圖

1.2.2.2 流程解析
一個mr程序啓動的時候,最先啓動的是MRAppMaster,MRAppMaster啓動後根據本次job的描述信息,計算出需要的maptask實例數量,然後向集羣申請機器啓動相應數量的maptask進程

maptask進程啓動之後,根據給定的數據切片範圍進行數據處理,主體流程爲:
利用客戶指定的inputformat來獲取RecordReader讀取數據,形成輸入KV對
將輸入KV對傳遞給客戶定義的map()方法,做邏輯運算,並將map()方法輸出的KV對收集到緩存
將緩存中的KV對按照K分區排序後不斷溢寫到磁盤文件

MRAppMaster監控到所有maptask進程任務完成之後,會根據客戶指定的參數啓動相應數量的reducetask進程,並告知reducetask進程要處理的數據範圍(數據分區)

Reducetask進程啓動之後,根據MRAppMaster告知的待處理數據所在位置,從若干臺maptask運行所在機器上獲取到若干個maptask輸出結果文件,並在本地進行重新歸併排序,然後按照相同key的KV爲一個組,調用客戶定義的reduce()方法進行邏輯運算,並收集運算輸出的結果KV,然後調用客戶指定的outputformat將結果數據輸出到外部存儲

1.3 MapTask並行度決定機制
maptask的並行度決定map階段的任務處理併發度,進而影響到整個job的處理速度
那麼,mapTask並行實例是否越多越好呢?其並行度又是如何決定呢?

1.3.1 mapTask並行度的決定機制
一個job的map階段並行度由客戶端在提交job時決定
而客戶端對map階段並行度的規劃的基本邏輯爲:
將待處理數據執行邏輯切片(即按照一個特定切片大小,將待處理數據劃分成邏輯上的多個split),然後每一個split分配一個mapTask並行實例處理

這段邏輯及形成的切片規劃描述文件,由FileInputFormat實現類的getSplits()方法完成,其過程如下圖:

1.3.2 FileInputFormat切片機制
1、切片定義在InputFormat類中的getSplit()方法
2、FileInputFormat中默認的切片機制:
簡單地按照文件的內容長度進行切片
切片大小,默認等於block大小
切片時不考慮數據集整體,而是逐個針對每一個文件單獨切片
比如待處理數據有兩個文件:
file1.txt 320Mfile2.txt 10M

經過FileInputFormat的切片機制運算後,形成的切片信息如下:
file1.txt.split1-- 0~128file1.txt.split2-- 128~256file1.txt.split3-- 256~320file2.txt.split1-- 0~10M

3、FileInputFormat中切片的大小的參數配置
通過分析源碼,在FileInputFormat中,計算切片大小的邏輯:Math.max(minSize, Math.min(maxSize, blockSize)); 切片主要由這幾個值來運算決定
minsize:默認值:1 配置參數: mapreduce.input.fileinputformat.split.minsize
maxsize:默認值:Long.MAXValue 配置參數:mapreduce.input.fileinputformat.split.maxsize
blocksize
因此,默認情況下,切片大小=blocksize
maxsize(切片最大值):
參數如果調得比blocksize小,則會讓切片變小,而且就等於配置的這個參數的值
minsize (切片最小值):
參數調的比blockSize大,則可以讓切片變得比blocksize還大

選擇併發數的影響因素:
運算節點的硬件配置
運算任務的類型:CPU密集型還是IO密集型
運算任務的數據量
1.4 map並行度的經驗之談
如果硬件配置爲2*12core + 64G,恰當的map並行度是大約每個節點20-100個map,最好每個map的執行時間至少一分鐘。
如果job的每個map或者 reduce task的運行時間都只有30-40秒鐘,那麼就減少該job的map或者reduce數,每一個task(map|reduce)的setup和加入到調度器中進行調度,這個中間的過程可能都要花費幾秒鐘,所以如果每個task都非常快就跑完了,就會在task的開始和結束的時候浪費太多的時間。
配置task的JVM重用可以改善該問題:
(mapred.job.reuse.jvm.num.tasks,默認是1,表示一個JVM上最多可以順序執行的task
數目(屬於同一個Job)是1。也就是說一個task啓一個JVM)

如果input的文件非常的大,比如1TB,可以考慮將hdfs上的每個block size設大,比如設成256MB或者512MB

1.5 ReduceTask並行度的決定
reducetask的並行度同樣影響整個job的執行併發度和執行效率,但與maptask的併發數由切片數決定不同,Reducetask數量的決定是可以直接手動設置:

//默認值是1,手動設置爲4
job.setNumReduceTasks(4);

如果數據分佈不均勻,就有可能在reduce階段產生數據傾斜
注意: reducetask數量並不是任意設置,還要考慮業務邏輯需求,有些情況下,需要計算全局彙總結果,就只能有1個reducetask

儘量不要運行太多的reduce task。對大多數job來說,最好rduce的個數最多和集羣中的reduce持平,或者比集羣的 reduce slots小。這個對於小集羣而言,尤其重要。

1.6 MAPREDUCE程序運行演示
Hadoop的發佈包中內置了一個hadoop-mapreduce-example-2.4.1.jar,這個jar包中有各種MR示例程序,可以通過以下步驟運行:
啓動hdfs,yarn
然後在集羣中的任意一臺服務器上啓動執行程序(比如運行wordcount):
hadoop jar hadoop-mapreduce-example-2.4.1.jar wordcount /wordcount/data /wordcount/out
2. MAPREDUCE實踐篇(1)
2.1 MAPREDUCE 示例編寫及編程規範
2.1.1 編程規範
用戶編寫的程序分成三個部分:Mapper,Reducer,Driver(提交運行mr程序的客戶端)
Mapper的輸入數據是KV對的形式(KV的類型可自定義)
Mapper的輸出數據是KV對的形式(KV的類型可自定義)
Mapper中的業務邏輯寫在map()方法中
map()方法(maptask進程)對每一個<K,V>調用一次(文本文件,value爲一行)
Reducer的輸入數據類型對應Mapper的輸出數據類型,也是KV
Reducer的業務邏輯寫在reduce()方法中
Reducetask進程對每一組相同k的<k,v>組調用一次reduce()方法
用戶自定義的Mapper和Reducer都要繼承各自的父類
整個程序需要一個Drvier來進行提交,提交的是一個描述了各種必要信息的job對象

1.7.2 wordcount示例編寫
需求:在一堆給定的文本文件中統計輸出每一個單詞出現的總次數
(1)定義一個mapper類
//首先要定義四個泛型的類型//keyin: LongWritable valuein: Text//keyout: Text valueout:IntWritablepublic class WordCountMapper extends Mapper<LongWritable, Text, Text, IntWritable>{ //map方法的生命週期: 框架每傳一行數據就被調用一次 //key : 這一行的起始點在文件中的偏移量 //value: 這一行的內容 @Override protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { //拿到一行數據轉換爲string String line = value.toString(); //將這一行切分出各個單詞 String[] words = line.split(" "); //遍歷數組,輸出<單詞,1> for(String word:words){ context.write(new Text(word), new IntWritable(1)); } }}

(2)定義一個reducer類
//生命週期:框架每傳遞進來一個kv 組,reduce方法被調用一次 @Override protected void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException { //定義一個計數器 int count = 0; //遍歷這一組kv的所有v,累加到count中 for(IntWritable value:values){ count += value.get(); } context.write(key, new IntWritable(count)); }}

(3)定義一個主類,用來描述job並提交job
public class WordCountRunner { //把業務邏輯相關的信息(哪個是mapper,哪個是reducer,要處理的數據在哪裏,輸出的結果放哪裏……)描述成一個job對象 //把這個描述好的job提交給集羣去運行 public static void main(String[] args) throws Exception { Configuration conf = new Configuration(); Job wcjob = Job.getInstance(conf); //指定我這個job所在的jar包// wcjob.setJar("/home/hadoop/wordcount.jar"); wcjob.setJarByClass(WordCountRunner.class); wcjob.setMapperClass(WordCountMapper.class); wcjob.setReducerClass(WordCountReducer.class); //設置我們的業務邏輯Mapper類的輸出key和value的數據類型 wcjob.setMapOutputKeyClass(Text.class); wcjob.setMapOutputValueClass(IntWritable.class); //設置我們的業務邏輯Reducer類的輸出key和value的數據類型 wcjob.setOutputKeyClass(Text.class); wcjob.setOutputValueClass(IntWritable.class); //指定要處理的數據所在的位置 FileInputFormat.setInputPaths(wcjob, “hdfs://hdp-server01:9000/wordcount/data/big.txt”); //指定處理完成之後的結果所保存的位置 FileOutputFormat.setOutputPath(wcjob, new Path(“hdfs://hdp-server01:9000/wordcount/output/”)); //向yarn集羣提交這個job boolean res = wcjob.waitForCompletion(true); System.exit(res?0:1); }

2.2 MAPREDUCE程序運行模式
2.2.1 本地運行模式
mapreduce程序是被提交給LocalJobRunner在本地以單進程的形式運行
而處理的數據及輸出結果可以在本地文件系統,也可以在hdfs上
怎樣實現本地運行?寫一個程序,不要帶集羣的配置文件(本質是你的mr程序的conf中是否有mapreduce.framework.name=local以及yarn.resourcemanager.hostname參數)
本地模式非常便於進行業務邏輯的debug,只要在eclipse中打斷點即可

如果在windows下想運行本地模式來測試程序邏輯,需要在windows中配置環境變量:
%HADOOP_HOME% = d:/hadoop-2.6.1
%PATH% = %HADOOP_HOME%\bin
並且要將d:/hadoop-2.6.1的lib和bin目錄替換成windows平臺編譯的版本

2.2.2 集羣運行模式
將mapreduce程序提交給yarn集羣resourcemanager,分發到很多的節點上併發執行
處理的數據和輸出結果應該位於hdfs文件系統
提交集羣的實現步驟:
A、將程序打成JAR包,然後在集羣的任意一個節點上用hadoop命令啓動
$ hadoop jar wordcount.jar cn.itcast.bigdata.mrsimple.WordCountDriver inputpath outputpath
B、直接在linux的eclipse中運行main方法
(項目中要帶參數:mapreduce.framework.name=yarn以及yarn的兩個基本配置)
C、如果要在windows的eclipse中提交job給集羣,則要修改YarnRunner類

mapreduce程序在集羣中運行時的大體流程:

附:在windows平臺上訪問hadoop時改變自身身份標識的方法之二:

  1. MAPREDUCE中的Combiner
    combiner是MR程序中Mapper和Reducer之外的一種組件
    combiner組件的父類就是Reducer
    combiner和reducer的區別在於運行的位置:
    Combiner是在每一個maptask所在的節點運行
    Reducer是接收全局所有Mapper的輸出結果;
    (4) combiner的意義就是對每一個maptask的輸出進行局部彙總,以減小網絡傳輸量
    具體實現步驟:
    自定義一個combiner繼承Reducer,重寫reduce方法
    在job中設置: job.setCombinerClass(CustomCombiner.class)
    (5) combiner能夠應用的前提是不能影響最終的業務邏輯
    而且,combiner的輸出kv應該跟reducer的輸入kv類型要對應起來

MAPREDUCE原理篇(2)
3.1 mapreduce的shuffle機制
3.1.1 概述:
mapreduce中,map階段處理的數據如何傳遞給reduce階段,是mapreduce框架中最關鍵的一個流程,這個流程就叫shuffle;
shuffle: 洗牌、發牌——(核心機制:數據分區,排序,緩存);
具體來說:就是將maptask輸出的處理結果數據,分發給reducetask,並在分發的過程中,對數據按key進行了分區和排序;

3.1.2 主要流程:
Shuffle緩存流程:

shuffle是MR處理流程中的一個過程,它的每一個處理步驟是分散在各個map task和reduce task節點上完成的,整體來看,分爲3個操作:
分區partition
Sort根據key排序
Combiner進行局部value的合併

3.1.3 詳細流程
maptask收集我們的map()方法輸出的kv對,放到內存緩衝區中
從內存緩衝區不斷溢出本地磁盤文件,可能會溢出多個文件
多個溢出文件會被合併成大的溢出文件
在溢出過程中,及合併的過程中,都要調用partitoner進行分組和針對key進行排序
reducetask根據自己的分區號,去各個maptask機器上取相應的結果分區數據
reducetask會取到同一個分區的來自不同maptask的結果文件,reducetask會將這些文件再進行合併(歸併排序)
合併成大文件後,shuffle的過程也就結束了,後面進入reducetask的邏輯運算過程(從文件中取出一個一個的鍵值對group,調用用戶自定義的reduce()方法)

Shuffle中的緩衝區大小會影響到mapreduce程序的執行效率,原則上說,緩衝區越大,磁盤io的次數越少,執行速度就越快
緩衝區的大小可以通過參數調整, 參數:io.sort.mb 默認100M

3.1.4 詳細流程示意圖

3.2. MAPREDUCE中的序列化
3.2.1 概述
Java的序列化是一個重量級序列化框架(Serializable),一個對象被序列化後,會附帶很多額外的信息(各種校驗信息,header,繼承體系。。。。),不便於在網絡中高效傳輸;
所以,hadoop自己開發了一套序列化機制(Writable),精簡,高效

3.2.2 Jdk序列化和MR序列化之間的比較
簡單代碼驗證兩種序列化機制的差別:
public class TestSeri { public static void main(String[] args) throws Exception { //定義兩個ByteArrayOutputStream,用來接收不同序列化機制的序列化結果 ByteArrayOutputStream ba = new ByteArrayOutputStream(); ByteArrayOutputStream ba2 = new ByteArrayOutputStream(); //定義兩個DataOutputStream,用於將普通對象進行jdk標準序列化 DataOutputStream dout = new DataOutputStream(ba); DataOutputStream dout2 = new DataOutputStream(ba2); ObjectOutputStream obout = new ObjectOutputStream(dout2); //定義兩個bean,作爲序列化的源對象 ItemBeanSer itemBeanSer = new ItemBeanSer(1000L, 89.9f); ItemBean itemBean = new ItemBean(1000L, 89.9f); //用於比較String類型和Text類型的序列化差別 Text atext = new Text(“a”); // atext.write(dout); itemBean.write(dout); byte[] byteArray = ba.toByteArray(); //比較序列化結果 System.out.println(byteArray.length); for (byte b : byteArray) { System.out.print(b); System.out.print("😊; } System.out.println("-----------------------"); String astr = “a”; // dout2.writeUTF(astr); obout.writeObject(itemBeanSer); byte[] byteArray2 = ba2.toByteArray(); System.out.println(byteArray2.length); for (byte b : byteArray2) { System.out.print(b); System.out.print("😊; } }}

3.2.3 自定義對象實現MR中的序列化接口
如果需要將自定義的bean放在key中傳輸,則還需要實現comparable接口,因爲mapreduce框中的shuffle過程一定會對key進行排序,此時,自定義的bean實現的接口應該是:
public class FlowBean implements WritableComparable
需要自己實現的方法是:
/** * 反序列化的方法,反序列化時,從流中讀取到的各個字段的順序應該與序列化時寫出去的順序保持一致 / @Override public void readFields(DataInput in) throws IOException { upflow = in.readLong(); dflow = in.readLong(); sumflow = in.readLong(); } /* * 序列化的方法 */ @Override public void write(DataOutput out) throws IOException { out.writeLong(upflow); out.writeLong(dflow); //可以考慮不序列化總流量,因爲總流量是可以通過上行流量和下行流量計算出來的 out.writeLong(sumflow); } @Override public int compareTo(FlowBean o) { //實現按照sumflow的大小倒序排序 return sumflow>o.getSumflow()?-1:1; }

3.3. MapReduce與YARN
3.3.1 YARN概述
Yarn是一個資源調度平臺,負責爲運算程序提供服務器運算資源,相當於一個分佈式的操作系統平臺,而mapreduce等運算程序則相當於運行於操作系統之上的應用程序
3.3.2 YARN的重要概念
yarn並不清楚用戶提交的程序的運行機制
yarn只提供運算資源的調度(用戶程序向yarn申請資源,yarn就負責分配資源)
yarn中的主管角色叫ResourceManager
yarn中具體提供運算資源的角色叫NodeManager
這樣一來,yarn其實就與運行的用戶程序完全解耦,就意味着yarn上可以運行各種類型的分佈式運算程序(mapreduce只是其中的一種),比如mapreduce、storm程序,spark程序,tez ……
所以,spark、storm等運算框架都可以整合在yarn上運行,只要他們各自的框架中有符合yarn規範的資源請求機制即可
Yarn就成爲一個通用的資源調度平臺,從此,企業中以前存在的各種運算集羣都可以整合在一個物理集羣上,提高資源利用率,方便數據共享

3.3.3 Yarn中運行運算程序的示例
mapreduce程序的調度過程,如下圖

  1. MAPREDUCE實踐篇(2)
    4.1. Mapreduce中的排序初步
    4.1.1 需求
    對日誌數據中的上下行流量信息彙總,並輸出按照總流量倒序排序的結果
    數據如下:
    1363157985066 13726230503 00-FD-07-A4-72-B8:CMCC 120.196.100.82 24 27 2481 24681 2001363157995052 13826544101 5C-0E-8B-C7-F1-E0:CMCC 120.197.40.4 4 0 264 0 2001363157991076 13926435656 20-10-7A-28-CC-0A:CMCC 120.196.100.99 2 4 132 1512 2001363154400022 13926251106 5C-0E-8B-8B-B1-50:CMCC 120.197.40.4 4 0 240 0 200
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章