reducetask的并行度----分区:
yarchild-- 1maptask---MyMapper/1reducetask任务----MyReducer
运行reduce任务的并行度
目前情况下 reducetask只有一个
19557 YarnChild --- reducetask
默认情况下 reducetask的个数只有1个
一个reducetask只能运行在一个节点上
当数据量很大时候 只有一个reducetask不合理的
1)这个reducetask的压力很大
2)负载不均衡
reducetask也应该根据我们的实际的数据 设置多个
如何设置:
代码设置:
job.setNumReduceTasks(tasks);
参数代表就是reducetask的个数 需要几个reducetask的时候 设置为几就可以
这个参数值 默认为1 默认情况只运行一个reducetask
job.setNumReduceTask;
发现 输出的结果3个文件 1个标志文件
part-r-00000
part-r-00001
part-r-00002
结论:一个reducetask最终输出一个对应的结果文件
reducetask中的数据如何划分的:
默认情况下:
hash分割数据的
key.hash%reducetask的个数
一个reducetask的数据对应的是一个分区的数据
分区:对map输出的数据进行一个按照一定的规则划分 每一部分称为一个分区
partition
这里的分割规则叫分区算法
推断:默认的分区算法:hash算法 1)散列 2)唯一
底层实现:
默认的抽象类 Partitioner抽象类 定义分区算法/分区规则的
public abstract class Partitioner<KEY, VALUE> {
//返回值:int 这里的返回值代表的是分区编号 每一个分区的唯一标志 默认从0开始 顺序递增的
//参数:参数1-map输出的key 参数2-map输出的value 参数3-分区个数(job.setNumReduceTask())
public abstract int getPartition
(KEY key, VALUE value, int numPartitions);
}
默认调用的实现类:HashPartitioner
//泛型指的是map输出的k v的类型
public class HashPartitioner<K, V> extends Partitioner<K, V> {
//默认的参数3 numReduceTasks
/** Use {@link Object#hashCode()} to partition. */
public int getPartition(K key, V value,
int numReduceTasks) {
return (key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
}
}
key.hashCode() & Integer.MAX_VALUE 目的:防止溢出 范围控制integer_max
1011001100111010101010101
&
111111111111111111111111
--------------------------
011001100111010101010101
默认的分区算法 取map的key的hash值%reducetask的个数 获取返回值就是分区编号
总结:默认情况下
一个分区----一个reducetask----一个输出结果文件
part-r-00001 00001---代表分区编号
默认情况下 分区算法 hash分区
(key.hashCode() & Integer.MAX_VALUE) % numReduceTasks;
默认情况下 分区算法 和 reducetask的个数一一对应的
reducetask的并行度 取决于reducetask的个数 job.setNumReduceTasks
reducetask的数据如何分配 取决于分区算法的 默认的hash分区
自定义分区:
默认的分区算法 并不能满足所有的需求 不能自定义数据的去向的
假设:淘宝数据
江苏
浙江
上海
内蒙
按照地域进行划分reducetask的数据 默认的就不可以 需要自定义分区
写法:
1)继承Partitioner类
2)重写getPartition 方法 这个方法的返回值 代表就是分区编号
案例:
将流量汇总统计结果按照手机归属地不同省份输出到不同文件中
手机号的前三位---手机号归属地
输出到不同的文件-----不同的reducetask的输出---不同分区
分区算法:按照手机号的归属地进行划分的
归属地不同---不同分区--不同reducetask---不同文件
自定义分区:
分区字段map的key的位置
按照手机号分区
shuffle
map端:
key:手机号
value:剩下的
reduce端:
输出
//指定自定义分区
job.setPartitionerClass(MyPartitioner.class);
//指定reducetask的个数
job.setNumReduceTasks(3);
报错:
分区编号 值3 reducetask-3个 redutask0--分区0 reducetask1=分区1 reducetask2=分区2
报错:
Illegal partition for 15013685858 (3)
原因 分区和reducetask的个数不匹配
注意:设定分区编号的时候 最好顺序递增的 不要跳数
分区个数3 个 reducetask的个数可以设置:
1=====可以 对应一个输出结果
2=====不可以
3可以
>3 可以
reducetask的个数 为1 的时候 所有的分区的数据 默认全部到一个reducetask中
reducetask的个数大于1 按照分区编号 对应的分区到对应的reducetask中
设置的时候reducetask的个数=分区个数
分区算法:
默认分区:key.hash%reducetask的个数
自定义分区:自己定义的
分区算法决定map输出的数据 如何分配给reduce的
数据倾斜:
多个reducetask并行计算的时候 某一个reducetask分配数据不均匀 这个时候产生现象数据倾斜
后果:reducetask的整体运行的性能低
100reducetask
99reducetask--1T 10min
1reducetask-----100T 1000min
进度:
map 100% reduce 99%
map 100% reduce 99%
map 100% reduce 99%
map 100% reduce 99%
map 100% reduce 99%
map 100% reduce 99%
map 100% reduce 99%
map 100% reduce 99%
...............
大数据中不怕数据量大 怕数据倾斜
很快reducetask达到90% 卡
数据倾斜一定需要尽量避免的
原因:分区算法没有合理的分配均匀数据
解决:合理设计分区算法 根据实际的数据抽样做测试 进行合理设计分区 业务、数据
mapreduce阶段:
maptask:
1个切片默认128M---1maptask
1)maptask的并行度比较高
切片个数---数据块的个数(最后一个切片有可能跨块的)
2)maptask数据一个切片 128M 对应的数据量比较小
reducetask:
容易产生数据倾斜
1)并行度不高
job.setNumReduceTasks()
经验值:reducetask最大值 datanode*0.95 100---95
2)reducetask的数据分配取决于分区算法
3)reducetask的对应的数据量本身比较大
combiner组件:
这个组件不适用所有的场景的
优化组件:提升性能的作用 这个组件默认不加的
作用:对到reducetask之前的数据做预处理 帮助reducetask做预处理 减少reducetask的数据量 提升性能
默认没有组件:
怎么加?
这个组件就是帮助reducetask做事情 减轻redcetask的压力
提前帮助reducetask做一些自己的工作
combiner业务逻辑和reducetask的逻辑一样 作用的时间点在maptask之后 reducetask之前
怎么实现?
这个组件不会影响业务逻辑
和reducetask的实现是一样
1)继承Reducer类
2)重写reduce方法
3)job中设置添加combiner组件
job.setCombinerClass(MyCombiner.class);
实现 和reducetask的实现一样 实际的开发过程直接使用reducer的类作为combiner的类
combiner的可以适用:
求和
求最大值
求最小值
Combiner不使用场景:
求平均值
注意:这个组件使用的时候 慎重考虑
分组:
map----shuffle-----reduce
shuffle:分区----排序----分组
分组到底是怎么分组的:按照map的key进行分组的
默认的类型:
wc:
000000000000000000000000000
hadoop----------------64
hadoop----------------64
hadoop----------------64
hadoop----------------64
000000000000000000000000000
hadoophello----------------1
hadoophello----------------1
hadoophello----------------1
hadoophello----------------1
相同的key为一组
默认的是key一直随着value指针变化而变化的 只不过默认的key都是一样的
自定义的类型:
111111111111111111111111111111
computer huangzitao 72.42857142857143-----------(null)
computer huangxiaoming 72.42857142857143-----------(null)
computer huangzitao 72.42857142857143-----------(null)
computer huangxiaoming 72.42857142857143-----------(null)
默认的分组按照自定义类型的comparaTo的方法进行分组的
现根据科目 再根据 成绩
将相同科目 和 相同分数的人划分到一组
111111111111111111111111111111computer huangzitao 72.42857142857143 //这一组中的第一个key
map:context.write(sb,null)
computer huangzitao 72.42857142857143-----------(null)
computer huangxiaoming 72.42857142857143-----------(null)
computer huangzitao 72.42857142857143-----------(null)
computer huangxiaoming 72.42857142857143-----------(null)
reduce:
默认分组:调用的就是所有类型ComparaTo方法---排序的方法
有的需求中 分组和排序如果有冲突,怎么办?
案例:
3、求出每门课程参考学生平均成绩top3的学生的信息:课程,姓名和平均分
课程 姓名 成绩
computer liujialing 98
computer huangbo 96
computer liutao 95
math huangdatou 99
math liuyifei 98
math liutao 95
map的输出的key:自定义类型(课程 姓名 成绩)
排序:按照成绩
分组:课程
comparaTo(){
先按课程
成绩
}
实际的上的分组:comparaTo()
按照:课程+成绩
每个课程的前三 保证reduce一次性可以接受的数据是同一门课程的
排序 和 分组 冲突了
分组不能使用刚才的排序的规则的了
排序:按照成绩
分组:课程
自定义分组:
分组:课程
分组的底层 对map输出所有的key进行比较 相同的(return 0)key 认为一组
比较的过程中 返回的!=0 认为不是同一组
writableComparator 分组规则的 普通类
默认的分组的比较:
@SuppressWarnings("unchecked")
//map输出的key-----比较 序列化 WritableComparable
//参数:WritableComparable
//分组针对map输出的key
public int compare(WritableComparable a, WritableComparable b) {
//默认的分组就是调用 自定义的comparaTo
return a.compareTo(b);
}
自定义分组:重写compare
1)继承writableComparator
2)重写compare()
3)job中指定
job.setGroupingComparatorClass(MyGroup.class);
报错:
java.lang.Exception: java.lang.NullPointerException
空指针异常的错误:
compare() 两个参数的对象默认不会帮我们创建的
/** Construct for a {@link WritableComparable} implementation. */
protected WritableComparator(Class<? extends WritableComparable> keyClass) {
this(keyClass, null, false);
}
protected WritableComparator(Class<? extends WritableComparable> keyClass,
boolean createInstances) {
this(keyClass, null, createInstances);
}
//参数1 比较的对象类型的class ScoreBean
//参数2 配置文件对象
//参数3---是否创建WritableComparable 这个对象 默认false 代表不创建
protected WritableComparator(Class<? extends WritableComparable> keyClass,
Configuration conf,
boolean createInstances) {
排序:分数
分组:课程
=========================
computer huangjiaju 83.2-----------(null)
computer huangjiaju 83.2-----------(null)
computer liutao 83.0-----------(null)
=========================
math huangxiaoming 83.0-----------(null)
=========================
english huanglei 83.0-----------(null)
=========================
computer liutao 83.0-----------(null)
先进行的排序 在进行的分组
排完序:
computer huangjiaju 83.2-----------(null)
computer huangjiaju 83.2-----------(null)
computer liutao 83.0-----------(null)
math huangxiaoming 83.0-----------(null)
english huanglei 83.0-----------(null)
computer liutao 83.0-----------(null)
分组在排序的基础上进行的:判断相邻的
computer huangjiaju 83.2-----------(null)
computer huangjiaju 83.2-----------(null)
computer liutao 83.0-----------(null)
math huangxiaoming 83.0-----------(null)
english huanglei 83.0-----------(null)
computer liutao 83.0-----------(null)
排序的时候需要将分组的字段放在一起
排序:
课程 分数
分组:
课程
既有排序 a b 又有分组 c
排序:c a b
分组:c
排序的字段中必须包含 分组字段 分组字段必须在排序字段的前面
补充:
reduce中的迭代器:
1)只能迭代一次 迭代的过程中指针的操作
只要迭代一次 指针调到末尾
2)迭代器中的数据对应的一组的所有的value
这个迭代器中每一个value都对应一个自己的key
111111111111111111111111111111computer huangzitao 72.42857142857143
computer huangzitao 72.42857142857143-----------(null)
computer huangxiaoming 72.42857142857143-----------(null)
computer huangzitao 72.42857142857143-----------(null)
computer huangxiaoming 72.42857142857143-----------(null)
==============computer huangxiaoming 72.42857142857143
每一组中的这个key是一直随着value指针移动而移动的
每一个value都会对应一个key值