0 MR简介
MapReduce用于处理海量数据计算,由谷歌论文而来,从论文角度来讲是一种思想,从技术角度来讲。是一种计算框架
1 MR代码规范
1.1 Mapper类
主要设计业务逻辑要什么输出
1.用户定义的Mapper类要继承父类
2.Mapper业务逻辑写在map()方法
3.Mapper输入数据是泛型KV对的形式,输出也是
Key 是偏移量,value 是值(内容)
4.Map()方法对每个<K,V>调用一次
在WordCount中一个<K,V>代表一行,即每行调用一次Map()方法
1.2 Reducer类
前两点和Mapper通用:要继承父类、重写reduce方法
1.输入数据类型对应Mapper输出类型
2.对每一组相同k的<k,v>组调用一次reduce()方法
有几个k就reduce几次
reduce()方法 参数如下:
<key,Iterable<序列化类型> Values (Iterable是集合,因为可能需要累加等操作),outK自定义,outV自定义 >
1.3 Driver类
概念:
类似一种八股文的写法:
2 MR序列化
MR 序列化类型都是对象!
用自己的序列化类型原因是如下,紧凑、快速、互操作性高
序列化的作用:主要用于不同服务器传输(知识点可看CG笔记)
-----------------------------------------------------------------------------------------
3 MR框架原理
3.1 并行度(关系到分区)
MapTask数量:切片数量决定
可通过公式修改切片大小,本地32m。
还有读取方式不同比如TextInputFormat、NLineInputFormat也会影响切片的数量
ReduceTask数量:非自定义分区情况下,ReduceTask影响分区数量
分区Partition
默认分区HashParitioner,按照Key值的hash值%NumReduceTask个数
drive:job.setNumReduceTask(x);
ReduceTasks可选:0(无Reduce)、1(不分区)、n(要求等于自定义分区数)
自定义时分区,若小于分区数则报错,大于分区数则多出空输出文件
Q:先分区还是先判断ReduceTasks?
A:先判断ReduceNum,不大于1则不执行分区。
Map
切片FileInputFormat、提交Job、Yarn打开MapTask、调用输入组件(TextInputFormat)、调用Map()方法
Mapper Shuffle
Partition分区、OutPutCollector写入环形缓冲区、溢写(之前进行快排Sort、可选合并Combine)、合并溢写文件Merge(归并排序)
Reduce Shuffle
(Copy)拉取相应分区文件 -> (Sort)Merge归并排序 -> (Reduce)分组(Group)
Reduce
集合聚合
Read -> Map->
Mapper Shuffle分区Partition -> OutPutCollector环形缓冲区 -> 溢写(溢写之前 快速排序Sort,可选合并Combine) -> 合并Merge归并排序 -> Reduce Shuffle(Copy)拉取相应分区文件 -> (Sort)Merge归并排序 -> (Reduce)分组(Group)
-> Reduce
环形缓冲区:默认100m,80%后溢写。一边存索引,一边存取真实的kv值。
Spill溢写:80%溢写,不100%的原因是因为溢写完成才空余RAM,这样得Map输出受限
·溢写完成后会产生两个文件,一个out文件,一个index文件,用于Reduce判断拉取数据。
MapperShuffler排序:快排,排列对象是KV对的索引,按字典顺序
Combine合并:分区内提前Reduce,是一种优化手段,继承Reduce。慎重使用,如求中位数不一定一样了
Merge归并排序:对已经有序的序列进行排序最优。
·第一个是合并溢写的文件,第二个是合并不同分区拉过来的文件
MAP压缩:在Merge之后,落盘前
Group分组:因为执行完毕后Key是排序的,一次读一组,values聚合成集合
·有一个辅助方法也可以实现排序(非系统默认实现的)
4 数据IO
4.1 FileInputFormat 切片机制
切片是MAP的最小计算单元,一般等同于块大小。(Local模式一般为32m)
4.2 数据输入
FileInputFormat(文件输入)后,可以选择如何输入进Map()方法,继承关系如下:
默认输入组件TextInputFormat,调用里面的RecorderReader读取数据
处理小文件:CombineTextInputFormat,把多个文件合并到一起统一切片
4.3 FileInputFormat机制源码解析
4.4 OutputFormat
5 MR排序
Key必须支持排序,MR执行期间自动排序,执行完毕后,key都是有序的。
排序种类
1)部分排序
每个分区的key是有序的,但分区和分区之间是无序的
2)全排序
最终输出只有一个文件,不仅每个分区的key是有序的,而且分区和分区也是有序的
·生产环境下慎用!文件量太大,聚合在一个reduce顶不住
3)二次排序
在自定义排序过程中,如果compareTo中判断条件为两个则为二次排序
·比如总流量相同,再按上行流量再排序
两种情况
排序代码在序列化类型中修改,因为排序讨论对象为Key
1.原生的序列化类型,已经实现Comparable接口,在源代码中重写comparaTo
方法提供默认排序规则
2.自定义序列化类型,若作为Key,定义时必须implements Comparable接口,重写comparaTo方法,之后提供默认排序规则。若需要自定义排序规则则按业务逻辑重写。
默认顺序
数字从小到大排序
字符串按字典顺序排序
6 Join
1 Reduce Join
标记不同来源的文件,将相同字段作为key进行多表连接。
自定义序列化Bean:
需要包含多表的所有字段(无需重复)+ flag文件来源。
Map:
1、Steup()初始化获取输入文件
FileSplit split = (FileSplit) context.getInputSplit();
fileName = split.getPath().getName();
2、不同文件分别处理(切割)
3、封装Bean对象输出。如pd表仅两个字段,其余other表才有的字段可封装nullWriteable
此时Other和pd每行都有各自的Bean对象,进入Reduce后聚合成想要的数据
Reduce:
1、创建两个集合
·现在有3个集合,传来的Bean(赋值用),新建的2个
2、遍历传来的Bean,判断来源是哪个表,赋值给两个新定义的集合
3、将想要整合的字段,从另一个集合取出get,封装到另一个集合
4、输出封装的集合,由于所有字段都在,所以V为NullWriteable
缺点:这种方式中,合并的操作是在Reduce阶段完成,Reduce端的处理压力太大,Map节点的运算负载则很低,资源利用率不高,且在Reduce阶段极易产生数据倾斜。
2 Map Join
Map Join适用于一张表非常小,一张表十分大的场景。
在Map端缓存多张表,提前处理业务逻辑,这样增加Map端业务,减少Reduce端数据的压力,尽可能的减少数据倾斜。
Driver
// 加载缓存数据
job.addCacheFile(new URI("file:///D:/input/tablecache/pd.txt"));
// Map端Join的逻辑不需要Reduce阶段,设置reduceTask数量为0
job.setNumReduceTasks(0);
Map
1、Setup获取缓存的文件
2、开流从中读数据并赋值给集合
3、从原表取出(切割)需要的字段,并从刚才赋值的集合取出需要的字段,一起连接封装
outK.set(words[0] + "\t" + pname + "\t" + words[2]);
//放弃pid,改为缓存中取出的name
7 ETL 数据清洗
比如清洗掉不符合手机号规则的手机,过短的日志文件。
7.1 数据清洗过程往往只需要运行Map,不需要写Reduce类。
·一般在Map中写一个校验规则,输出即可
·筛选一般输入<K,V>后MAP仅输出<V,NullWriteable>
7.2 各类正则校验规则(可百度)能符合大多数企业下的应用场景
8 压缩
8.1 压缩概念
Hadoop处理大量数据时,可通过CPU分担一定的IO压力,可以在计算和空间中寻求一个性价比最高的状态。
1、计算密集型程序:减少压缩机制
2、IO密集型程序:多使用压缩
8.2 Hadoop的压缩算法
Snappy要求较高:Liunx7+、Hadoop3.0+、IDEA需要配置
8.3 使用
Driver:
// 设置map端输出压缩开启
configuration.setBoolean("mapreduce.map.output.compress", true);
// 设置map端输出压缩方式
configuration.setClass("mapreduce.map.output.compress.codec",
BZip2Codec.class,CompressionCodec.class);
// configuration.setClass("mapreduce.map.output.compress.codec",
GzipCodec.class,CompressionCodec.class);
// configuration.setClass("mapreduce.map.output.compress.codec",
DefaultCodec.class,CompressionCodec.class);
/*************************************************************************/
// 设置reduce端输出压缩开启
FileOutputFormat.setCompressOutput(job, true);
// 设置压缩的方式
FileOutputFormat.setOutputCompressorClass(job, BZip2Codec.class);
// FileOutputFormat.setOutputCompressorClass(job, GzipCodec.class);
// FileOutputFormat.setOutputCompressorClass(job, DefaultCodec.class);
9 总结
完成一个MR就是组装、设计的过程
自定义相应的 序列化、分区、排序,写相应Map、Reduce。
进阶还可以自定义IO