Python开发MapReduce系列(二)Python实现MapReduce分桶

版权声明:本文为博主原创文章,未经博主允许不得转载
 
首先,先引出两点来展开下面的话题。
(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=2 \    
    -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架构设计与实现原理》
posted @ 2017-09-24 10:22  那一抹风  阅读(4757)  评论(0编辑  收藏  举报