版权声明:本文为博主原创文章,未经博主允许不得转载
首先,先引出两点来展开下面的话题。
(1)map阶段的排序是在hash之后,写入磁盘之前进行。排序的两个关键字是partition(分区编号)和key。
(2)map结束后,并不是马上写到磁盘的,而是有个环形缓冲区,数据写到缓冲区中,默认溢出率是80%(这个值可以通过属性设置 io.sort.mb),每达到溢出条件就溢出生成一个小文件,直到全部数据写完,最后把所有的小文件合并成一个大文件,并写到磁盘中。这样做的目的是减少磁盘寻道时间,让每个map只输出一个文件,并为这个文件提供索引文件,记录下每个reduce对应数据的偏移量.(其实就是为map与reduce之间的分发建立映射关系)
1、默认情况介绍
在hadoop streaming的默认情况下,是以”\t”作为分隔符的。对于标准输入来说,以每行读取到的数据的第一个”\t”为分界线, 在其之前的部分为key,在其之后的为value。如果一个 "\t" 字符没有,则整行都被当做是key处理。
2、MapReduce shuffler过程中的sort和partition阶段
mapper阶段除了用户代码,最重要的是shuffle 过程,这个过程是MapReduce耗时和消耗资源的主要地方,因为其涉及到磁盘的写入等操作。这里先不谈优化方面的处理,只研究shuffle 过程中的sort和partition两个过程。为什么只研究这两个过程,因为,sort和partition是MapReduce的核心思想,整个过程就是在不断的重复 排列和分割 的操作。
从第1点可以知道,MapReduce的key默认是以 \t 分割得到的,我们能不能根据自己的需要来获取到特定形式的key?实现类似分桶、根据指定列的排序之类的自由排序呢?答案是可以的。我们可以通过以下的参数来实现:
3、相关的参数介绍
3.1map阶段
-jobconf mapred.reduce.tasks=2【此属性针对下面的例子都有效】 map.output.key.field.separator:指定map输出<key,value>对之后,其中key内部的分割符。 num.key.fields.for.partition:指定分桶时,按照分隔符切割后,用于分桶key所占的列数。 -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner:前两个参数,要配合这个partitioner,没有的话会报错 例如:map.output.key.field.separator = , num.key.fields.for.partition = 2 -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner 一行数据:1 , 2 , 3 , 4 , 5(在这里1 2 之间的逗号是key内部的分隔符,并且1,2格式key的数据分为到同一桶) stream.map.output.field.separator: map中的key与value的分隔符 stream.num.map.output.key.fields:指定map输出按照分隔符切割后,key所占有的列数,之前的是key,之后的是value 例如:map.output.key.field.separator = , num.key.fields.for.partition = 2 -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner stream.map.output.field.separator = : stream.num.map.output.key.fields = 3 输入:1 , 2 , 3 , 4 , 5 1 , 2 , 2 , 4 , 5 1 , 3 , 4 , 4 , 5 1 , 3 , 3 , 4 , 5 输出part-00000:1 , 2 , 2 : 4 , 5 1 , 2 , 3 : 4 , 5 输出part-00000:1 , 3 , 3 : 4 , 5 1 , 3 , 4 : 4 , 5 1 , 2 是分桶值,1 , 2 , 3是key, 4 , 5是value。在这里1 2 之间的逗号是key内部的分隔符,1 , 2格式key的数据分为到同一桶
3.2 reduce阶段
stream.reduce.output.field.separator:reduce中key与value的分隔符 stream.num.reduce.output.key.fields:reduce中分隔符的位置
3、分桶测试
run.sh脚本(作为一个会偷懒的程序猿,能偷懒就偷懒,写个脚本省掉每次写入一大串指令的烦恼)
HADOOP_CMD="/home/hadoop/hadoop/bin/hadoop" STREAM_JAR_PATH="/home/hadoop/hadoop/contrib/streaming/hadoop-streaming-1.2.1.jar" INPUT_PATH_A="/a.txt" INPUT_PATH_B="/b.txt" OUTPUT_PATH="/output" $HADOOP_CMD fs -rmr $OUTPUT_PATH #mapreduce在运行时,文件系统不能存在output目录(目录名字随意) $HADOOP_CMD jar $STREAM_JAR_PATH -input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B -output $OUTPUT_SORT_PATH -mapper "python map.py" -reducer "python red.py" -file ./map.py -file ./red.py -jobconf mapred.reduce.tasks=2 -jobconf map.output.key.field.separator=, -jobconf num.key.fields.for.partition=1 \ -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner -jobconf stream.map.output.field.separator=: -jobconf stream.num.map.output.key.fields=3
a.txt内容
1,2,3:hadoop 1,2,1:hadoop 1,2,5:hadoop 1,3,4:hadoop 1,2,9:hadoop 1,2,11:hadoop 1,2,7:hadoop 1,3,15:hadoop 1,3,14:hadoop 1,2,19:hadoop
b.txt内容
1,2,0:java 1,2,2:java 1,2,8:java 1,3,4:java 1,2,2:java 1,2,14:java 1,2,12:java 1,3,1:java 1,3,5:java 1,2,3:java
4、结果输出
【part-00000】输出内容如下:
1,2,0:java 1,2,1:hadoop 1,2,2:java 1,2,2:java 1,2,3:hadoop 1,2,3:java 1,2,5:hadoop 1,2,7:hadoop 1,2,8:java 1,2,9:hadoop 1,2,11:hadoop 1,2,14:java 1,2,19:hadoop
【part-00001】输出内容如下:
1,3,1:java 1,3,4:hadoop 1,3,4:java 1,3,5:java 1,3,14:hadoop 1,3,15:hadoop
5、结果分析
由结果可以看出:
(1)以前2列为分桶标志,因为part-00000,part-00001分别以1,2和1,3开头。
(2)以前3列为key,并且第3列为分桶之后排序的key。
(3)key内部之间是以 , 分隔。
(4)key与value之间是以 : 分隔。
参考:
(1)《Hadoop技术内幕:深入解析MapReduce架构设计与实现原理》