以处理text文件为例
map端
map端:
1、MRApplicationMaster启动maptask,并分配其一个编号,maptask根据编号寻找对应的存储文件位
置信息及切片长度的数据切片,默认数据切片对应的文件大小与blocksize大小一致。然后maptask
再根据数据切片寻找对应数据文件信息。然后maptask会通
org.apache.hadoop.mapreduce.InputFormat接口的TextInputFormat实现类的
createRecordReader()方法创建LineRecordReader对象,通过该对象的nextKey()和
nextValue()方法从hdfs上逐行读取数据文件内容,key为该行数据的偏移量,value为该行数据。
再交由mapper类进行数据的处理。
2、mapper类对数据处理完后会调用Context对象的write()方法将数据写出,而
org.apache.hadoop.mapred.OutputCollector类则负责将这些数据收集放入环形缓冲区中。数据
进入缓冲区前会通过org.apache.hadoop.mapred.Partitioner类进行分区,分区方式为对key取
hash再对reduce数量取模。这就使得一个分区对应于一个reducetask。分区之后数据文件文件会序
列化成字节数组,进入环形缓冲区中。每个maptask都有一个环形缓冲区,默认大小为100MB,环形缓
冲区本质上就是一个字节数组。
3、当环形缓冲区达到80%满的时候,启动一个单独的Spiller线程锁定环形缓冲区这80%的内存,进行溢
写操作,其余20%的内存仍然可以接收数据的输入。每次溢写被执行后,都会形成一个小文件,当数据
写入影响到了溢写时,环形缓冲区的数据输入会阻塞。在溢写真正被触发执行前,会先通过
org.apache.hadoop.util.QuickSort类先对环形缓冲区中的数据进行一个二次快速排序,先按分
区排,再按key排,排序结束后单个分区内数据按key有序。
4-a、如果设定了combiner,并且当前maptask已经溢写形成的小文件数目不小于3,则触发combiner,
combiner会对环形缓冲区内的数据做一次按key聚合操作,减少溢写到磁盘的数据量(不改变溢写的
总文件数,这点要理解)。每一次溢写都是按分区号逐个进行的,溢写操作会直到最后一份数据溢写
完成后结束。
5-a、整个maptask的所有溢写操作完成后会对当前maptask溢写产生的所有小文件执行merge操作,将其
merge成一个大的数据文件和索引文件。整个merge过程用到了外部排序和归并排序两种排序算法,
merge结束,该数据文件内每个分区内的数据都有序排列。然后删除所有spill中间文件后,
当前maptask结束。
4-b、如果设定了combiner,并且当前maptask溢写形成的小文件数目小于3,则直接执行溢写操作,溢
写按分区号逐个进行,溢写操作会直到最后一份数据溢写完成后结束。
5-b、整个maptask的所有溢写操作完成后会对当前maptask溢写产生的所有小文件执行merge操作,将其
merge成一个大的数据文件和索引文件。整个merge过程用到了外部排序和归并排序两种排序算法,
merge结束,该数据文件内每个分区内的数据都有序排列。然后删除所有spill中间文件后进入
combiner阶段,combiner会对数据文件内的每一个分区数据做一次按key聚合操作,减少输入到
reduce端的数据量。combiner完成则当前maptask结束。
注意事项:
merge前,A小文件的1号分区有{a:2},B小文件的1号分区有{a:5},merge之后就成了C大文件1号分区{a:[2,5]}。
merge不是一蹴而就的,每次只merge10个文件,由mapreduce.task.io.sort.factor控制。
combiner前,A小文件的1号分区有{a:1},{a:4},B小文件的1号分区有{b:1},{b:3},combiner之后就形成了A小文件的1号分区有{a:5},B小文件的1号分区有{b:4}。
####以上注意事项请好好理解
####集群很繁忙的情况下设置了combiner,也可能不生效。
#####所有溢写小文件和merge后的大文件都是senquence文件
reduce端
reduce端:
1、MRApplicationMaster启动reducetask,并给其分配一个编号,reducetask根据编号通过http协
议拉取对应的分区数据,该分区数据可能位于所有的maptask中,所以reducetask会启动多个线程拉
取数据,最多5个,由参数mapreduce.reduce.shuffle.parallelcopies控制。每一个
reducetask都有一个内存缓冲区,当在某个map端拉取的数据大小小于内存缓冲区大小的25%,则进
入内存缓冲区中,否则直接进入磁盘。
2、当内存缓冲区的数据文件达到1000个(mapreduce.reduce.merge.inmem.threshold)或者内存缓冲
区的数据占用内存比达到66%(mapreduce.reduce.shuffle.merge.percent)的时候,会在内存中
执行merge操作。如果设定了combiner,则merge过程中也会执行combiner操作,这两个操作结束,
内存缓冲区中的数据溢写到磁盘形成小文件,溢写的次数可能很多次,且有一些maptask拉取过来的文
件不经过内存缓冲区直接存在磁盘,所以磁盘上的小文件可能有很多个。由由于merge过程会使用归并
排序,所以单个溢写小文件中的数据也是有序排列的。
3、当当前reducetask的所有数据全部拉取并在磁盘形成小文件后,会将所有的小文件merge成一个大文
件,merge过程会使用归并排序,所以形成的大文件也是有序的。整个merge过程不是一蹴而就的,每
次merge只会合并10个文件,由mapreduce.task.io.sort.factor控制,假设有50个文件,则要执
行6次merge。
4、merge之后Reducer类通过迭代器获取数据的key-value键值对,知道一个key对应的所有键值对全部
获取玩会执行reduce方法处理,处理结束通过Context对象的write方法将处理结果写出,但它并非
直接写出,而是会判断配置文件中配置的输出文件类型后再写出,如果没有配置,则reducetask会
通过org.apache.hadoop.mapreduce.InputFormat接口的TextInputFormat实现类的
getRecordWriter()方法创建LineRecordWriter对象,通过该对象的write()方法将该key对应
数据写出到hdfs的结果文件中。则然后处理下一个key对应的数据,直到当前reducetask的所有数据
全部被处理完成且正确输出后,当前reducetask结束。
#####输出到hdfs上时,每一对key-value对应一行数据,tab键分隔。
#####如果当前reducetask的编号为0,则产生的输出文件就为part-r-000000,为1,则产生的输出文件就为part-r-000001
#####过程中涉及到两次merge,这种情况发生在maptask特别多的情况下。在极少maptask的情况下,设甚至是可以没有merge的。
#####所有溢写小文件和merge后的大文件都是senquence文件
hadoop调优
1、Mapper阶段
减少溢写次数:
参数:io.sort.mb io.spill.percent
减少merge次数:
参数:io.sort.factor
开启combiner:
在不影响业务的情况下。
2、Reducer阶段
合理设置mapper和reducer数:
mapper数量取决于切片大小和文件大小,切片大小不好改变,文件大小也不好改变。
reducer数则可以按照需要设置,我们可以设置每个reducer处理的数据量多大。
设置maper和reducer共存,让reducer提前运行:
参数:mapreduce.job.reduce.slowstart.completedmaps 默认0.05
3、在各阶段合理配置压缩
map输入:bzip2/lzo 要支持分割
map输出:lz4 压缩解压缩时间段
map输出:
最终输出:bzip4 体积小
非最终输出:bzip2/lzo 要支持分割
参考:
https://blog.csdn.net/pengzonglu7292/article/details/79419021
其他的调优就调不了了,应该在hive中调。