Hadoop-MapReduce-学习日志-20181213

目录

1、MapReduce编程套路(有一张图重要)

2、maptask的并行度

3、MapReduce中自定义类

4、MapReduce程序的运行模式

5、MapReduce案例包

6、排序

7、combiner

8、分区—reducetask的并行度

9、join:hql——>sql

10、分组

11、shuffle的原理解析(有一个张图重要)

12、多job串联

13、倒排索引

14、全局计数器

15、MapReduce的输入

16、注意事项

练习作业


1、MapReduce编程套路(有一张图重要)

在mapreduce编程中,map输出的key的设计很重要。为什么呢?因为mapreduce中的核心在shuffle过程,而shuffle过程会进行分区、排序、分组 、Combiner,这几个过程全部是根据map输出的key的设计的。

那么,如何设计map输出的key呢?那就看reduce中接收的时候,想怎么样进行分组,按照哪一个字段进行分组。分组的标志性词汇是每一、每类。例如:词频统计中,每一个单词的出现的总次数,那么分组的条件就是单词,所以,以单词作为map输出的key,那么在reduce中相同的单词的一次性全部接受到了,就可以进行这个单词的词频统计了。

案例:求所有数据的最大值、最小值、平均值,那么就需要将所有的数据分到一组中(reduce方法时一组调用一次的),所以设计如下:

map端:map输出的key值相同的 key:“1”,value:每一个数值

reduce端:一组的所有数据,接受的key:“1”,接受的value:所有的数值,循环遍历接受的value的迭代器求最大值、最小值、平均值

运行命令:

hadoop jar jar包路径 主类的全限定名 代码运行需要的参数(如果多个参数 用空格隔开)

hadoop jar data.jar com.ghgj.cn.mapreduce.Driver

结果文件命名:

part-r-00000 r-reduce 结果是reduce端输出的

part-m-00000 m-map 结果是map端输出的

 

mapreduce的编程套路:

输入 FileInputFormat

-TextInputFormat

-RecordReader

-LineRecordReader

getCurrentKey---LongWritable

getCurrentValue---Text

MyMapper<LongWritable,Text,.....>

map(key,value,context){

/*

*1.reduce 2.hdfs

/

context.write()

}

shuffle

分区 Partitioner

排序 WritableComparable

分组 WritableComparator

优化 Combiner 合并

排好序 分好组的内容 给reduce

MyReducer<>

一组调用一次

reduce(key,values,context){

context.write

}

输出:

FileOutputFormat

-TextOutputFormat

-RecordWriter

-LineRecordWriter

 

2、maptask的并行度

Number of Maps = 5

什么是maptask?运行Mapper代码的一个任务称为一个maptask

maptask的特点是什么?

1)一个maptask任务启动一个进程YarnChild

2)maptask是map端执行任务的基本单位,不可进行分割的

3)一个maptask任务只能在一个节点上运行,不能分到两个或多个节点运行

4)一个节点上可以运行多个maptask任务的。

什么是maptask的并行度?maptask并发运行了多少个。

 

执行一个计算程序,到底执行多少个maptask任务,和什么有关?节点?和节点无关的,数据量?和数据量有关的。

一个maptask任务对应多少数据量的呢?

1)如果300M启动一个maptask,那么数据存在跨块、跨节点访问,需要进行网络传输,导致性能低。

2)如果100M启动一个maptask任务,那么仍然存在数据的跨块、跨节点访问的问题

所以,分析之后得出的结论:一个maptask任务最好对应一个数据块的大小,默认128M。事实上,一个maptask任务对应的数据是一个切片的数据。

什么是切片split?

1)切片是逻辑上的概念,仅仅相当于一个偏移量范围的划分,并没有真正的进行切分

2)切片是进行maptask任务时数据划分的基本单位,即一个maptask对应一个split,一个maptask对应一个yarnchild进程。

通过上面的分析,可以推测一个切片对应的数据大小就是一个数据块的大小,默认128M。

底层实现:计算切片大小的方法

protected long computeSplitSize(long blockSize, long minSize,long maxSize) {

//Math.max(1, Math.min(long的最大值, 128))

return Math.max(minSize, Math.min(maxSize, blockSize));

}

所以,切片大小就是128M。切片对应的就是一个数据块,如果一个文件不够切片大小,单独划分一个切片,一个文件的最后一个切片有可能大于一个块大小的(128M),最大可以达到128*1.1=140.8M,因此最后一个切片有可能存在跨节点跨块访问数据的。

面试:切片与切块的区别是什么?

切片:逻辑概念,是maptask运行的基本数据划分单元

切块:物理概念,是hdfs存储时候基本存储单位

理论上一个切片的大小等于一个块的大小。

如何修改切片大小呢?

若想让切片大于128M,则修改MinSize >128M

若想让切片小于128M,则修改maxSize <128M

修改方法:

1)修改配置文件 mapred-site.xml

<property>

<name>mapreduce.input.fileinputformat.split.maxsize</name>

<value>100*1024*1024</value>

</property>

<property>

<name>mapreduce.input.fileinputformat.split.minsize</name>

<value>200*1024*1024</value>

</property>

2)代码中

FileInputFormat.setMaxInputSplitSize(job, size);

FileInputFormat.setMinInputSplitSize(job, size);

 

3、MapReduce中自定义类

在hadoop中内置的类型:

ntWritable

LongWritable

DoubleWritable

ByteWritable

Text

NullWritable

.......

hadoop的所有数据类型都必然经过网络传输或固化磁盘,所以这些数据类型必须具备序列化和反序列化的能力。所以hadoop中内置的数据类型都具备这个能力。如何实现的呢?内置的数据类型实现了Writable接口,这是一个序列化和反序列化的标志接口。因此,如果在hadoop中自定义数据类型,自定义的数据类型也要实现Writable接口。

案例:统计每一个用户(手机号)所耗费的总上行流量、总下行流量、总流量

分组条件:手机号

map端:

key:手机号

value:上行+下行

1)拼串

2)封装一个对象

reduce端:

接受的数据:同一个手机号的所有数据接受一次

循环遍历values求和

自定义类:

1)实现Writable

2)重写序列化和反序列化的方法 write() 和 readFields()

3)get/set/无参构造/有参构造/toString

 

4、MapReduce程序的运行模式

1)打jar包方式

先打jar包——>放置服务器——>运行jar包

缺点:不便于代码调试

2)本地运行

windows运行的是本地模式,并没有提交到集群运行

优点:便于代码调试

3)本地运行并提交yarn(集群)上

配置环境很复杂,需要修改源代码

 

5、MapReduce案例包

位置:/home/hadoop/apps/hadoop-2.7.6/share/hadoop/mapreduce/hadoop-mapreduce-examples-2.7.6.jar

wordcount pi(π)

执行命令:hadoop jar hadoop-mapreduce-examples-2.7.6.jar wordcount /in /wc_example01

 

6、排序

默认情况下会按照map输出的key进行排序,排序规则是:

1)字符串类型按照字典顺序升序排序

2)数值类型按照数值大小升序排序。

需求1:对wc的结果进行排序 按照单词出现的次数进行倒序排序

hadoop 15

hello 10

hive 10

lily 5

spark 10

word 5

ww 5

在shuffle中只会对map输出的key进行排序,如果想要对单词的次数进行排序,那么需要将单词的出现次数放在map输出的key中。所以:

map端:

key:词频

value:单词

shuffle:对词频进行排序

reduce端:

接受到的就是排序完成的数据

按照顺序输出就可以

将map输入的结果反置

思考这个问题:目前key输出的类型hadoop中的内置类型,为甚么可以进行排序?内置类型具备排序的能力

public class IntWritable implements WritableComparable<IntWritable>

public interface WritableComparable<T> extends Writable, Comparable<T>

内置的类型还要具备序列化和反序列化的能力

Writable:序列化和反序列化能力的接口

Comparable:比较能力的接口

因此,如果我们自定义的类型想要具备比较的能力,需要实现WritableComparable,然后重写write、readFields以及comparaTo方法

注意:自定义的类型想要进行比较,则需要放在map输出的key中,如果自定义类型放在map输出的key中,那么自定义类型必须实现WritableComparable

需求2:将统计结果按照先按照上行流量排序,若上行流量相同,则按照总流量倒序排序

map端:

key:自定义类

value:null

reduce端:

输出

注意:自定义的类作为key进行shuffle阶段的排序分组时,会认为排序的规则中返回值为0 的时候就认为相同的key就会分到一组。

 

7、combiner

默认没有这个组件的,这个组件是一个shuffle过程中的优化组件。

combiner的主要作用:

1)就是减少shuffle过程的数据量

2)减少reduce端接受的数据量

combiner是怎么做到的呢?

1)针对每一个maptask的结果做一个局部合并

2)针对每一个maptask的结果提前提前进行一次局部的计算,

3)实质上这个计算可以理解为帮reduce减轻负担的,这个的业务逻辑和reduce端一样的

combiner组件可以理解为对每一个maptask的输出结果局部的计算,reducer这里就可以理解为对所有的maptask的结果做的全局计算。

如何实现combiner呢?combiner的过程发生在mapper之后reducer之前的(shuffle),这个组件业务逻辑和reduce的一样的

1)继承Reducer的类

前两个的泛型和mapper的输出的 k,v对应,后两个泛型和reducer的输入的 k,v对应,因此前两个泛型的类型和后两个泛型的类型一致的。

2)重写reduce方法,业务和reducer一样,combiner组件不会影响原来的mapper和reducer的业务逻辑

3)在job中指定:job.setCombinerClass(MyCombiner.class);

注意:

1)combiner组件仅仅是一个优化的组件,就是帮助reduce减少数据量

2)不要combiner组件添加额外的功能

3)一般我们写的时候combiner组件的代码复用reducer的代码,要求combiner的前两个泛型和后两个泛型一致

combiner适合/不适合的场景总结:

1)可以使用的场景:求和、求最大值、求最小值等

2)不使用的场景:平均值、平方等

 

8、分区—reducetask的并行度

什么是reducetask?运行Reducer代码的一个任务,是reduce端运行任务的基本单位。

什么是reducetask的并行度?一个job中reducetask运行的总个数。

默认情况下reducetask的并行度是1,即默认情况下只启动一个reducetask任务,这意味着这一个reducetask处理所有的maptask的输出结果,而一个reducetask只能在一个节点上运行,当maptask的数量过多,造成reducetask的压力过大,负载不均衡。

<property>

<name>mapreduce.job.reduces</name>

<value>1</value>

<description>The default number of reduce tasks per job. Typically set to 99%

of the cluster's reduce capacity, so that if a node fails the reduces can

still be executed in a single wave.

Ignored when mapreduce.jobtracker.address is "local".

</description>

</property>

这时候我们需要设置多个reducetask任务,设置方式:job.setNumReduceTasks(3);

结果文件:

part-r-00000

part-r-00001

part-r-00002

每一个结果文件对应的就是一个reducetask的输出。

每一个reducetask是如何进行分配数据的呢?job.setNumReduceTasks(3); 根据分区算法决定的

分区算法的类:Partitioner

默认的分区算法:

推断:默认的mapkey.hash%分区个数

默认情况下分区个数=reducetask的个数

默认的分区算法实现类:HashPartitioner

public int getPartition(K key, V value,int numReduceTasks) {

/*

* 011111111111111111111111

&1011001101101002101011011

* -----------------------------

* 1011001101101

*/

return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;

}

一个分区的数据对应一个reducetask,maptask的最终输出结果经过分区之后放到不同的分区中的,每一个reducetask到对应的分区中取对应自己reducatask的数据

结果文件命名:

part-r-00000(分区编号)

job.setNumReduceTasks(3);

0 1 2

part-r-00001

part-r-00002

分区算法决定是每一个分区中的数据如何分配的问题?

1)对于分区算法来说不怕数据量大,就怕数据分配不均匀,会造成数据倾斜的现象,极大的降低程序运行的性能,所以尽量避免。

2)合理设计分区算法,可以尽量避免数据倾斜现象

自定义分区:当分区算法不能满足需求的时候,需要自定义分区算法

步骤:

1)定义一个类继承Partitioner

2)重写getPartition方法 --- 分区算法

3)在job中指定reducetask的个数 job.setNumReduceTasks(4); 这里的reducetask的个数与分区是一一对应的

4)指定分区类 job.setPartitionerClass(MyPartitioner.class);

 

案例:将流量汇总统计结果按照手机归属地不同省份输出到不同文件中

输出到不同的文件中——多个reducetask——多个分区——多个分区中的数据分配规则(分区算法)

按照手机归属地进行分区

134-136 北京

137-138 武汉

139-150 西安

剩下 成都

map端:

key:手机号

value:其他

reduce端:

直接输出

分区编号——reducetask——输出结果文件的对应关系?

1)分区编号 和 reducetask的关系

reducetask的任务编号从0开始的,比如 job.setNumReduceTasks(4); 那么就有 reducetask0 reducetask1 reducetask2 reducetask3,对应的reducetask只会拿去和自己的reducetask编号相同的分区编号的数据

分区编号0----->reducetask0

分区编号1----->reducetask1

分区编号2----->reducetask2

分区编号3----->reducetask3

 

分区编号0----->reducetask0

分区编号1----->reducetask1

分区编号2----->reducetask2

reducetask3 0kb

分区编号4----->reducetask4

2)reducetask和输出结果的关系

比如 job.setNumReduceTasks(4); reducetask和最终的输出结果是一一对应的

reducetask0---->part-r-00000(reducetask的编号)

reducetask1---->part-r-00001

reducetask2---->part-r-00002

reducetask3---->part-r-00003

当分区个数4个的时候,即 0,1,2,4

1)运行过程中发现reducetask个数设置为1,这种情况是可以的,4个分区的数据全部到一个reducetask中了。

2)运行过程中发现reducetask的个数设置的大于1,对应的reducetask会去拉取对应的分区的数据。

3)运行过程中发现reducetask的个数设置2,3,4,则会报错

4)运行过程中发现reducetask的个数设置5,6,7 ,则可以正常执行

原则:一般设置分区的时候最好从0开始并顺序递增,设置reducetask的个数的时候=分区个数

 

9、join:hql——>sql

如何通过mapreduce程序实现类似 select a.*,b.* from a join b on a.id=b.id; 的操作呢?两种方式:reducejoin 和 mapjoin

1、reducejoin 表关联的操作全部在reduce端进行操作

案例:

订单数据表t_order: flag=0

id date pid amount

1001 20150710 P0001 2

1002 20150710 P0001 3

1003 20150710 P0002 3

Id:数据记录id

Date 日期

Pid 商品id

Amount 库存数量

商品信息表t_product flag=1

pid name category_id price

P0001 小米5 C01 2000

P0002 锤子T1 C01 3500

select a.*,b.* from t_order a join t_product b on a.pid=b.pid;

reduce端做关联,reduce端一次性可以接受关联建相同的两个表的数据,可以将两个表的数据放在一个目录下,所以分组条件就是关联建。具体如下:

map端:

获取到数据来源,最好在map函数之前可以获取文件来源,setup() 方法中获取文件的来源

key:分组条件 关联建 P0001

value:其他 打标记(区分数据来源)

A 1001 20150710 2

A 1002 20150710 3

B 小米5 C01 2000

reduce端:

将两个表的数据进行拼接

需要识别数据来自于哪一张表

思考:reducejoin的缺陷就是容易产生数据倾斜问题

 

2、mapjoin 表关联的操作全部在map端进行操作

reduce端的key的问题:

1)一组中的key

2)迭代器中的每一个value都会对应这一组中的一个key

3)最开始的时候key指的是这一组中的第一个key

reduce端的迭代器的问题:

1)只能循环遍历一次

2)底层就是一个指针的操作

3)一旦循环遍历一次之后,指针就到这一组的最后了,如果在进行循环遍历,什么内容也没有了

 

reducejoin缺陷:

1)reduce端并行度问题

job.setNumReducetasks() 人为决定的

经验值 reducetask的并行度不可以太高,太高的情况下反而性能很低

经验值 reducetask的个数<= datanode个数*0.95

2)容易产生数据倾斜

 

mapjoin的优点:

1)并行度很高,一个maptask对应就是一个逻辑切片默认128M,优先数据本地化。

2)不会产生数据倾斜的,每一个maptask对应的数据量比较小

 

mapjoin的实现:

1)需要将其中的一个表加载到运行每一个maptask任务的缓存中

2)在在maptask中读取另外一个表进行关联

3)关联完成直接输出

注意:运行代码的时候不需要reduce,请将reducetask的个数设置为0, job.setNumReduceTasks(0); 不加这一句默认执行一个reducetask的,结果:part-m-00000

 

mapjoin的缺点:map端做join的时候,加载到缓存中的表一定是小表,不能太大,适解决大*小和小*小的关联问题。

那么大*大如何做关联:

1)reducejoin——数据倾斜的问题

2)mapjoin——将其中的一个表进行切分,切成多个小表,即大*大——>多个小*大,然后分别进行关联

 

10、分组

分组默认调用的类WritableComparator

默认情况下的分组:

1)分组的本质是比较

2)分组是根据排完序之后的结果进行的,map端输出的‘相同’key已经排在一起了,只需要判断相邻元素返回值是否为0就可以:0表示同一组,不为0表示不是同一组。

public int compare(WritableComparable a, WritableComparable b) {

//参数代表的就是map输出的key

//参数1:map输出的当前的key,参数2:map输出的上一个key

//调用的就是map输出的key的compareTo(b)

return a.compareTo(b);

}

底层的默认的分组是按照map输出的key的comparaTo方法进行比较的,comparaTo返回值是否为0:0表示同一组,不是0表示不是一组。默认分组调用是排序的规则,若分组和排序的字段是同一个字段,就可以使用默认的。

案例:求每门课程平均分的前五名同学,典型的分组求每一组的topN(分组topN)问题。

分组条件:课程 key

排序排序:平均分 key

求:每一门课程的前五

方案一:

map端:

key:课程

value:学生姓名+平均分

reduce端:对同一门课程的所有学生排序,取前五,使用集合。可以解决但不是最佳方案。

方案二:

map的key进行拼串:

map端:

key:课程+成绩 Text

Text类型进行排序,先按照课程,再按照平均分

value:人名

reduce端:

按照map输出的key进行分组,即课程+成绩

课程+成绩相同的才会分到一组,不符合要求

由上面可知,分组和排序的规则不一样,分组不可以使用排序的规则的。所以,需要自定义分组规则。

如何定义分组呢?

1)继承WritableComparator

2)重写compare(key1,key2)

3)重写构造器,调用父类的构造器super(key.clss,true)

4)job中指定 job.setGroupingComparator()

自定义分组的使用场景?分组和排序的条件不一致时需要自定义分组

注意:

1)想要进行分组的字段必须提前排在一起

2)排序字段中必须包含分组字段并且分组字段还必须在排序字段的最前面

例如:

排序字段:A,B 分组字段:C

实际排序字段:C A B 分组字段:C

 

11、shuffle的原理解析(有一个张图重要)

shuffle阶段介绍:

1)mapreduce编程的核心

2)mapreduce运行的核心

3)map输出-----shuffle-----reduce输入

map的输出先到环形缓冲区中——底层实现byte[]。

reduce到对应分区区数据。

环形缓冲区的每一个溢写文件都是按照分区 map的key排好序的

同一个分区内部 相同的key数据在一起

如果有Combiner,会在两个地方调用:

1)溢写文件的时候,将每一个溢写文件的数据进行一次Combiner

2)每一个maptask的溢写文件进行归并的时候,将每一个maptask的最终结果进行Combiner

 

shuffle原理详解:

1、map端输出的结果文件通过收集器Collector,先输出到环形缓冲区中

环形缓冲区底层就是一个字节数组 byte[]

环形缓冲区默认大小100M,一个maptask就会对应一个环形缓冲区

环形缓冲区溢写阀值是0.8,即80M(原始数据,元数据)

元数据作用:方便排序

元数据内容:key的起始下标、value的起始下标、value的长度以及分区编号

2、环形缓冲区达到溢写阀值,就开始溢写,溢写之前会先进行一次排序(快排),先根据分区编号再根据map输出的key进行排序,相同的分区的数据在一起,相同分区中相同的key的数据在一起,排序仅仅调整的元数据的位置,排完序之后就开始进行溢写(线程),根据元数据顺序写原始数据。

3、每一个maptask对应多个溢写文件

4、对多个溢写文件进行归并

5、在归并的过程中也会排序(归并排序),结果文件按照分区排好序的,按照map输出的key排好序的。

6、reducetask从MRAPPMaster中获取maptask的运行进度,当获取一个maptask运行完成的时候就开始启动reducetask

7、对应的reducetask开启多线程,到对应的节点上抓取(fetch)maptask的对应的分区的数据

8、每一个reducetask就会抓取同一个分区的多个maptask的结果数据,进行将这些同一个分区的多个结果数据进行归并(归并排序)

9、最终每一个分区的文件只有一个,这个文件会经过分组之后给reducetask

 

12、多job串联

什么是多job串联?一个业务需求需要多个job(多个mapreduce任务)相互串联配合才可以完成,下一个job需要依赖上一个job的输出结果。

 

如何配置多job串联?

//JobControl 封装多个job,添加依赖关系

//参数:给封装的多个job起一个组名

JobControl jc=new JobControl("commonfriend");

//将job转换为CtrolledJob

//参数:配置文件,job的配置文件是job.xml

ControlledJob cjob1=new ControlledJob(job1.getConfiguration());

ControlledJob cjob2=new ControlledJob(job2.getConfiguration());

//将依赖关系指定,job1运行完成,job2才启动

cjob2.addDependingJob(cjob1);

//将job添加到jc中

jc.addJob(cjob1);

jc.addJob(cjob2);

//提交job

Thread t=new Thread(jc);

t.start();

//job是否执行完成,完成true,没有执行完成false

while(!jc.allFinished()){

Thread.sleep(100);

}

jc.stop();

 

13、倒排索引

什么是索引?类似于目录,如数据库索引表示某一列的值在表中出现的位置。

为什么使用索引?方便查询,提升查询性能

什么是倒排索引?为内容创建索引,表示在全文内容下,某一个单词在全文中出现的位置。

例如:

1.txt 内容:hadoop spark hello hadoop

2.txt 内容:hadoop spark hello hadoop

倒排索引:

hadoop 1.txt 1,4 2.txt 1,4

hello 1.txt 3 2.txt 3

案例:有两份数据,如下

mapreduce-4-1.txt

huangbo love xuzheng

huangxiaoming love baby huangxiaoming love yangmi

liangchaowei love liujialing

huangxiaoming xuzheng huangbo wangbaoqiang

mapreduce-4-2.txt

hello huangbo

hello xuzheng

hello huangxiaoming

题目一:编写MapReduce求出以下格式的结果数据:

统计每个关键词在每个文档中当中的第几行出现了多少次

例如,huangxiaoming关键词的格式:

huangixaoming mapreduce-4-1.txt:2,2; mapreduce-4-1.txt:4,1;mapreduce-4-2.txt:3,1

以上答案的意义:

关键词huangxiaoming在第一份文档mapreduce-4-1.txt中的第2行出现了2次

关键词huangxiaoming在第一份文档mapreduce-4-1.txt中的第4行出现了1次

关键词huangxiaoming在第二份文档mapreduce-4-2.txt中的第3行出现了1次

 

解决方案如下

第一步:

map端:

key:单词+文件名+行

value:1

reduce端:

将每一个文件的相同的行的数据 累加

huangixaoming mapreduce-4-1.txt:2,2

输出:

baby map01.txt 21 1

hello map02.txt 0 1

hello map02.txt 14 1

hello map02.txt 28 1

huangbo map01.txt 0 1

huangbo map01.txt 100 1

最终:

huangixaoming mapreduce-4-1.txt:2,2; mapreduce-4-1.txt:4,1;mapreduce-4-2.txt:3,1

第二步:

对单词做一个汇总:

map端:

key:单词

value:其他 文件名:行,次数

reduce端:

对同一个单词的所有的而结果进行拼接

 

14、全局计数器

全局计数器:用于统计maptask任务或reducetask任务运行过程中的参数数量

默认全局计数器:

File System Counters 文件系统的相关参数的全局统计

mapreduce运行过程中,数据读写的参数统计

FILE: Number of bytes read=2230

FILE: Number of bytes written=602319

Map-Reduce Framework mapreduce计算框架的全局统计

Map input records=32 map端输入的数据条数

Map output records=32 map端输出的数据条数

收集器收集的数据的条数

Map output bytes=865 map输出的字节数

Map output materialized bytes=935

Input split bytes=100 输入的切片的字节

combiner组件的相关参数

Combine input records=0 combiner输入的数据条数

Combine output records=0 combiner输出的数据条数

Reduce input groups=4 reduce输入的组数

Reduce shuffle bytes=935 reduce中shuffle过程的数据量

Reduce input records=32 reduce的输入的数据条数,没有combiner时等于map的输出,有combiner时等于combiner的输出

Reduce output records=20 reduce输出的数据条数

Spilled Records=64 溢写的数据条数

Shuffled Maps =1 经过shuffle的maptask的个数

Failed Shuffles=0 失败的shuffle的个数 maptask的失败的个数

Merged Map outputs=1

GC time elapsed (ms)=7

Total committed heap usage (bytes)=412090368

Shuffle Errors shuffle过程的错误计数器

BAD_ID=0

CONNECTION=0

IO_ERROR=0 ******

WRONG_LENGTH=0

WRONG_MAP=0

WRONG_REDUCE=0

File Input Format Counters 输入的计数器

Bytes Read=1255 所有的文件输入大小

File Output Format Counters 输出的计数器

Bytes Written=640 文件最终输出的大小

 

计数器的默认实现:

public enum TaskCounter {

MAP_INPUT_RECORDS,

MAP_OUTPUT_RECORDS,

MAP_SKIPPED_RECORDS,

MAP_OUTPUT_BYTES,

MAP_OUTPUT_MATERIALIZED_BYTES,

SPLIT_RAW_BYTES,

COMBINE_INPUT_RECORDS,

COMBINE_OUTPUT_RECORDS,

REDUCE_INPUT_GROUPS,

REDUCE_SHUFFLE_BYTES,

REDUCE_INPUT_RECORDS,

REDUCE_OUTPUT_RECORDS,

REDUCE_SKIPPED_GROUPS,

REDUCE_SKIPPED_RECORDS,

SPILLED_RECORDS,

SHUFFLED_MAPS,

FAILED_SHUFFLE,

MERGED_MAP_OUTPUTS,

GC_TIME_MILLIS,

CPU_MILLISECONDS,

PHYSICAL_MEMORY_BYTES,

VIRTUAL_MEMORY_BYTES,

COMMITTED_HEAP_BYTES

}

需求:统计所有数据中有缺失字段的数据条数。保证可以取到每一条原始的数据

自定义计数器:

1)定义一个枚举类,一个参数

2)在map端使用计数器,组相关的在reduce端

//计数器获取

Counter counter = context.getCounter(MyCounter.LOSS_FIELDS_RECORDS);

/*

* String getName(); 获取计数器的名字

* long getValue(); 获取计数器的值

* void setValue(long value); 给计数器赋值

* void increment(long incr); 累加操作,参数代表每次累加的数量

*/

//对计数器 +1

counter.increment(1);

应用场景:通常用于统计maptask任务中的一些错误记录的条数

 

15、MapReduce的输入

maptask的输入框架给的,其中,key:LongWritable,value:Text

默认输入

FileInputFormat

TextInputFormat

//获取文件读取器

@Override

public RecordReader<LongWritable, Text> createRecordReader(InputSplit split,TaskAttemptContext context) {

String delimiter = context.getConfiguration().get("textinputformat.record.delimiter");

byte[] recordDelimiterBytes = null;

if (null != delimiter)

recordDelimiterBytes = delimiter.getBytes(Charsets.UTF_8);

return new LineRecordReader(recordDelimiterBytes);

}

RecordReader

LineRecordReader

nextKeyValue

getCurrentkey

getCurrentvalue

需求:小文件合并,默认的输入一行一行的数据输入,目前的需求需要一次性读取一个小文件。

默认的文件的读取的方式无法满足的,需要自定义输入。

步骤:

1)继承FileInputFormat<自定义的输入的key,value的类型>

2)重写createRecordReader

3)定义文件读取器

继承RecordReader

重写getCurrentkey、getCurrentvalue、nextKeyValue

4)在job中指定自定义的输入 job.setInputFormatClass(MergeFileInputFormat.class);

 

自定义输出:

1)继承FileOutputFormat

2)重写getRecordWriter---RecordWriter

3)定义文件写出器

继承RecordWriter

重写 write

4)在job中指定

备注:有一些课堂代码值得学习,这里暂未看

 

16、注意事项

reduce端的values的两个坑:

1.只能循环遍历一次

2.values另外的坑:key values  只有一个对象

对象重用
List<Stu> list=new list;
for(Stu s:values){
重用的一个对象
循环遍历到每一个对象相当于对同一个对象重新赋值属性
list.add(s);
}

list中所有的对象都是values的最后一个对象了
所有的values通用一个内存地址,循环遍历的时候  只是属性在变

如何解决:
每次循环遍历的时候将这个values中的对象重新赋值给新的对象
for(Stu s:values){
Student ss=new Student(s.getName());
list.add(ss);
}
 

练习作业

1、求5个文件(小文件),每个文件中放的都是单词,每一行多个单词,分割符\t。求这五个文件中的所有单词的出现的总次数

2、有5个文件  每一个文件中存储的都是数字  每行一个字这五个文件中所有数字的最大值

3、mr的wc写完

4、5个文件  每一个文件中存储的都是数字  每行一个字这五个文件中所有数字的最大值 用MR写一遍

5、题目--求学生成绩--普通版,链接:

6、流量案例,链接:

7、题目--3--求学生成绩--增强版,链接:

8、很多作业题目,单独进行整理

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值