进阶实践4:  mapper,reducer输出数据压缩

应用场景
当mapper或者reducer的输出数量比较大,会影响shuffle阶段远程拷贝的网络性能,以及对存储容量的要求;这个时候可以考虑对mapper或者reducer的输出结果进行压缩

框架提供的压缩能力

 

能否指定压缩

能否指定压缩方式

         作用

Mapper输出

Yes

Yes

减少shuffle网络传输的数据量

Reducer输出

Yes

yes

减少占用的HDFS容量

 
 

重点是修改run.sh

 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"
INPUT_FILE_PATH="/05_mr_compression_input/The_Man_of_Property.txt"
OUTPUT_PATH="/05_mr_compression_output"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
# Compress output of map and reduce $HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH \
-output $OUTPUT_PATH \
-mapper "python map.py mapper_func WLDIR" \
-reducer "python red.py reduer_func" \
-jobconf "mapred.reduce.tasks=5" \ # 最终结果可以看到5个压缩文件
-jobconf "mapred.compress.map.output=true" \
-jobconf "mapred.map.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec" \ # map输出结果进行压缩 -jobconf "mapred.output.compress=true" \
-jobconf "mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec" \ # reduce输出结果进行压缩 -cacheArchive "hdfs://master:9000/w.tar.gz#WLDIR" \ # 将HDFS上已有的压缩文件分发给Task
-file ./map.py \ # 分发本地的map程序到计算节点
-file ./red.py # 分发本地的reduce程序到计算节点

-D 方式指定

 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"
INPUT_FILE_PATH="/05_mr_compression_input/The_Man_of_Property.txt"
OUTPUT_PATH="/05_mr_compression_output" $HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
# Compress output of map and reduce $HADOOP_CMD jar $STREAM_JAR_PATH \
-D mapred.reduce.tasks= \ #指定多个reduce,看输出结果是否为5个压缩文件
-D mapred.compress.map.output=true \
-D mapred.map.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec \
-D mapred.output.compress=true \
-D mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec \ -input $INPUT_FILE_PATH \
-output $OUTPUT_PATH \
-mapper “python map.py mapper_func WLDIR” \
-reducer “python red.py reducer_func” \
-cacheArchive “hdfs://master:9000/w.tar.gz#WLDIR” \ -file ./map.py \
-file ./red.py

查看job运行完成后的reduce结果

 

对于输出的5个压缩文件,通过hadoop fs –text 可以查看gz压缩文件中的内容


 

MR进阶实践5:  通过输入压缩文件,控制map个数

对于压缩文件,Inputformat将不进行split, 每个压缩文件对应1个map。因此将实践4输出的压缩文件,当做Map的输入文件,就可以验证map个数是否等于输入压缩文件个数

 
验证方法: 修改run.sh,将上一个实践的输出路径修改为本Job的input路径,mapper用简单的cat代替

注意:mapreducer的输入数据源可以是一个目录下的多个文件

HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"

STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"

INPUT_PATH="/05_mr_compression_output"  

# 上一个task的输出目录,所有文件都作为数据源,包括5个压缩文件,log文件,SUCCESS文
# 件夹, 由于log和SUCCESS是上一个文件的历史记录信息,会被框架自动过滤,因此只会启动处理压缩文件的5个
# map OUTPUT_PATH="/output cat"
#$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
# To verify map number by input compressed files $HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_PATH \
-output $OUTPUT_PATH\
-mapper "cat" \ # 不做任何处理,将输入数据直接输出
-jobconf "mapred.reduce.tasks=0" # 不需要任何reducer操作
由于mapper仅仅是将inputformat解压后的输入数据直接输出,并没有再额外配置map输出的压缩,输出的没有压缩的明文文件
 

 

MR进阶实践6:  输入多个文件,单Reducer排序

本质:全局排序

 
分析:输入文件为多个,并且每行为key,value形式,MapReduce框架会自动根据key (字符串形式) 进行排序;如果只有1个Reducer,则Reducer的输入此时已经有序,直接输出即可
 

要点:需要注意的是mapper后的排序以及reducer前的归并排序,都是对key进行字符串排序,因此会出现1, 10,110,2这样的排序结果,因此要在mapper和reducer中进行一定处理,才能得到类似数字的排序结果

 

原始数据

Mapper处理后数据

排序后Reducer前数据

Reducer后数据

1

1001

1001

1

2

1002

1002

2

10

1010

1003

3

20

1020

1010

10

3

1003

1020

20

 

Mapper: 对一行的key,value,   进行加1000操作,然后再将key转为字符串

Reducer: 对一行的key,value,  进行int(key)-1000操作,然后在将key转为字符串

 
输入数据源
# /a.txt
hadoop
hadoop
hadoop
hadoop
…………………………..
hadoop # /b.txt
java
java
java
java
…………………………..
java

run.sh

 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar" INPUT_FILE_PATH_A="/a.txt"
INPUT_FILE_PATH_B="/b.txt" # 2个数据源全部读取, inpuformat进行split
OUTPUT_SORT_PATH="/output_allsort_01"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH # 单Reducer实现全局排序
$HADOOP_CMD jar $STREAM_JAR_PATH \
-D mapred.reduce.tasks=1 \ # 单个recuder,利用框架自动排序的能力,完成全局排序
-input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B \ # 指定多个输入路径
-output $OUTPUT_SORT_PATH \
-mapper "python map_sort.py" \
-reducer "python red_sort.py" \
-file ./map_sort.py \
-file ./red_sort.py

map_sort.py

 #!/usr/local/bin/python
import sys
base_count = 1000 for line in sys.stdin:
key,val = line.strip().split('\t')
new_key = base_count + int(key)
print "%s\t%s" % (str(new_key), val)

reduce_sort.py

#!/usr/local/bin/python
import sys
base_value = 1000 for line in sys.stdin:
key, val = line.strip().split('\t')
print str(int(key)-1000) + "\t" + val 
 
输出结果
 

 

MR进阶实践7:  输入多个文件,全局逆向排序(单reducer)

本质:全局排序

分析:输入文件为多个,并且每行为key,value形式,由于MapReduce框架会自动根据key (字符串形式) 进行排序;如果只有1个Reducer,则Reducer的输入此时已经有序,直接输出即可

要点:需要注意的是mapper后的排序以及reducer前的归并排序,都是对key进行字符串排序,因此会出现1, 10,110,2这样的排序结果,因此要在mapper和reducer中进行一定处理,才能得到类似数字的排序结果

 

原始数据

Mapper处理后数据

排序后Reducer前数据

Reducer后数据

1

9998

9979

20

2

9997

9989

10

10

9989

9996

3

20

9979

9997

2

3

9996

9998

1

 

Mapper: 对一行的key,value,   进行9999-key操作,然后再将key转为字符串

Reducer: 对一行的key,value,  进行9999-int(key)操作,然后在将key转为字符串

 

输入数据源

# /a.txt
hadoop
hadoop
hadoop
hadoop
…………………………..
hadoop
# /b.txt
java
java
java
java
…………………………..
java

run.sh

 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"

 STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"

 INPUT_FILE_PATH_A="/a.txt"
INPUT_FILE_PATH_B="/b.txt" # 2个数据源全部读取, inpuformat进行split
OUTPUT_SORT_PATH="/output_allsort_01"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH # 单Reducer实现全局排序 $HADOOP_CMD jar $STREAM_JAR_PATH \
-D mapred.reduce.tasks= \
-input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B \ # 指定多个输入路径 ,分隔
-output $OUTPUT_SORT_PATH \
-mapper "python map_sort.py" \
-reducer "python red_sort.py" \
-file ./map_sort.py \
-file ./red_sort.py
 

map_sort.py

#!/usr/local/bin/python
import sys
base_count = 9999 for line in sys.stdin:
key,val = line.strip().split('\t')
new_key = base_count - int(key)
print "%s\t%s" % (str(new_key), val)
 

reduce_sort.py

 #!/usr/local/bin/python
import sys
base_value = 9999 for line in sys.stdin:
key, val = line.strip().split('\t')
print str(9999-int(key)) + "\t" + val
输出结果


 

MR进阶实践8:  输入多个文件,全局排序(多reducer)

本质:全局排序

分析单个Reducer的隐患,也算是比较明显Reducer的负载首先会很重,如果出现问题,整个Job都要重新来过,多Reducer可以做到负载分担,但是需要保证原本1个Reducer的输入,被划分到多个Reducer后,输出结果还是有序的

 
 
 

Key: 0~50

Key: 51~100

Key: 0~50

Reducer1

Key:51~100

Reducer2

 

要做到这样,我们就需要手工再构建一列“key”, 专门用于做partition阶段的分桶, 由它来保证实现上面的划分

 

Key-new,  key,   value

 

Key: 0~50

Key: 51~100

0      0~50         val

Reducer1

1      51~100     val

Reducer 2

 

其次在进行mapper端和reducer端排序的时候,要基于新key和原始key, 总共2列key来排序,从而实现同一reducer内部的原始key也是排序的,这样reducer端的代码只要将新增的key丢弃即可

 

run.sh

 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar" INPUT_FILE_PATH_A="/a.txt"
INPUT_FILE_PATH_B="/b.txt"
OUTPUT_SORT_PATH="/07_output_allsortNreducer"
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH # add in new column for partition, use 2 column as key for sort
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B\
-output $OUTPUT_SORT_PATH \
-mapper "python map_sort.py" \
-reducer "python red_sort.py" \
-file ./map_sort.py \
-file ./red_sort.py \
-jobconf mapred.reduce.tasks=2 \ # 多个reducer,进行全局排序
-jobconf stream.map.output.field.separator=' ' \
-jobconf stream.num.map.output.key.fields= \ #key有2列,新增+变换
-jobconf num.key.fields.for.partition= \ #只用key的第一列来分桶
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner #指定能基于key的某些列进行分桶的特定partitioner
 

map_sort.py

 #!/usr/local/bin/python
import sys
base_count = 1000 for line in sys.stdin:
key,val = line.strip().split('\t')
key = base_count + int(key) partition_id = 1
if key <= (1100+1000)/2:
partition_id = 0 # 0~50,pid=0; 51~100, pid=1
print "%s\t%s\t%s" % (str(partition_id), str(key), val)

reduce_sort.py

 #!/usr/local/bin/python
import sys
base_value = 1000 for line in sys.stdin:
partition_id, key, val = line.strip().split('\t')
print str(int(key)-1000) + "\t" + val #直接丢弃手工添加的partition_id

运行结果:

两个Reducer运行,会产生2个最终结果,其中1个文件会只包含key为0~50的记录,而另一个文件只会包含key为51~100的记录


 

MR进阶实践8:  多表Join

假定有2张表,表1记录了用户姓名和职位, 表2记录了用户姓名和年龄,如何通过mapreduce实现两张表的join, 进而得到:用户姓名职位 年龄
 
显然通过1个mapreduce作业是无法完成的,但可以拆分为多个mapreduce作业的方式来完成:如果表1和表2的记录合并在同一个文件,并且根据姓名排序,就会发现每个用户有2条记录,将第一条记录的内容缓存,然后再和第二条记录的内容合并,就可以完成Join的操作(以leftjoin为例), 但此时还需要考虑1个问题,要对同一个文件中的来自表1和表2的内容进行标注,才能保证leftjoin时,表1的内容在记录的前端,表2的内容在记录的后端,到这里,基本上这个问题就解决了
 

run.sh   拆分为3个mapreduce任务

 HADOOP_CMD="/usr/local/src/hadoop-1.2.1/bin/hadoop"

 STREAM_JAR_PATH="/usr/local/src/hadoop-1.2.1/contrib/streaming/hadoop-streaming-1.2.1.jar"

 INPUT_FILE_PATH_A="/a.txt"  #job1的数据源
INPUT_FILE_PATH_B="/b.txt" #job2的数据源
OUTPUT_A_PATH="/output_a"
OUTPUT_B_PATH="/output_b"
OUTPUT_JOIN_PATH="/output_join" $HADOOP_CMD fs -rmr -skipTrash $OUTPUT_A_PATH $OUTPUT_B_PATH $OUTPUT_JOIN_PATH
$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_JOIN_PATH # MapReduce Job1: 表1添加flag=, (key, , value)
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH_A \
-output $OUTPUT_A_PATH \
-mapper "python map_a.py" \
-file ./map_a.py # MapReduce Job2: 表2添加flag=, (key, , value)
$HADOOP_CMD jar $STREAM_JAR_PATH \
-input $INPUT_FILE_PATH_B \
-output $OUTPUT_B_PATH \
-mapper "python map_b.py" \
-file ./map_b.py # MapReduce Job3: cat做mapper, 每2条记录组成1个完整记录
# (key,,value) (key,, value)
# 使用第1列做分桶,使用1,2列做排序,通过reducer将两条记录合并 $HADOOP_CMD jar $STREAM_JAR_PATH \
-input $OUTPUT_A_PATH,$OUTPUT_B_PATH\
-output $OUTPUT_JOIN_PATH \
-mapper "cat" \
-reducer "python red_join.py" \
-file ./red_join.py \
-jobconf stream.num.map.output.key.fields= \ #2列做key
-jobconf num.key.fields.for.partition= \ #1列做分桶
-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner

第一个作业的map_a.py,  添加flag=1

 #!/usr/local/bin/python
import sys for line in sys.stdin:
key,value = line.strip().split('\t')
print "%s\t\t%s" % (key, value)

第二个作业的map_b.py,  添加flag=2

 #!/usr/local/bin/python
import sys
for line in sys.stdin:
key,value = line.strip().split('\t')
print "%s\t\t%s" % (key, value)

第3个mapreduce作业,将cat作为输入,因此mapper的输入是两张表记录的总和,并且同一个员工的两条记录在一起,并且来自表1的记录在前,来自表2的记录在后

Key1, 1, value1

Key1, 2, value2

Key2, 1, value1

Key2, 2 , value2

* partition基于第1列分桶,同一用户的记录就会由1个reducer处理

*key有2列,因此会基于2列key进行排序,保证表1的记录在前

 

第三个作业的reduce_join.py,  合并数据,丢弃添加的flag

 #!/usr/local/bin/python
import sys
cur_key = None
tem_val = ‘’ for line in sys.stdin:
key,flag, value = line.strip().split('\t')
flag = int(flag) #要做转换,否则没有任何输出 if cur_key == None and flag==1:
cur_key = key
tem_val = value
elif cur_key == key and flag==2:
print ‘%s\t%s\t%s’ %(cur_key, tem_val, value)
cur_key = None
tem_val = ‘’

最后将运行结果通过hadoop fs -get下载到本地,就可以看到两张表已经完成join操作

MapReduce-实践2的更多相关文章

  1. 大数据系列之分布式计算批处理引擎MapReduce实践-排序

    清明刚过,该来学习点新的知识点了. 上次说到关于MapReduce对于文本中词频的统计使用WordCount.如果还有同学不熟悉的可以参考博文大数据系列之分布式计算批处理引擎MapReduce实践. ...

  2. 大数据系列之分布式计算批处理引擎MapReduce实践

    关于MR的工作原理不做过多叙述,本文将对MapReduce的实例WordCount(单词计数程序)做实践,从而理解MapReduce的工作机制. WordCount: 1.应用场景,在大量文件中存储了 ...

  3. 大数据系列之数据仓库Hive命令使用及JDBC连接

    Hive系列博文,持续更新~~~ 大数据系列之数据仓库Hive原理 大数据系列之数据仓库Hive安装 大数据系列之数据仓库Hive中分区Partition如何使用 大数据系列之数据仓库Hive命令使用 ...

  4. Hadoop MapReduce开发最佳实践(上篇)

    body{ font-family: "Microsoft YaHei UI","Microsoft YaHei",SimSun,"Segoe UI& ...

  5. 化繁为简(三)—探索Mapreduce简要原理与实践

    目录-探索mapreduce 1.Mapreduce的模型简介与特性?Yarn的作用? 2.mapreduce的工作原理是怎样的? 3.配置Yarn与Mapreduce.演示Mapreduce例子程序 ...

  6. MapReduce 原理与 Python 实践

    MapReduce 原理与 Python 实践 1. MapReduce 原理 以下是个人在MongoDB和Redis实际应用中总结的Map-Reduce的理解 Hadoop 的 MapReduce ...

  7. 【原创 Hadoop&Spark 动手实践 3】Hadoop2.7.3 MapReduce理论与动手实践

    开始聊MapReduce,MapReduce是Hadoop的计算框架,我学Hadoop是从Hive开始入手,再到hdfs,当我学习hdfs时候,就感觉到hdfs和mapreduce关系的紧密.这个可能 ...

  8. Hadoop化繁为简(三)—探索Mapreduce简要原理与实践

    目录-探索mapreduce 1.Mapreduce的模型简介与特性?Yarn的作用? 2.mapreduce的工作原理是怎样的? 3.配置Yarn与Mapreduce.演示Mapreduce例子程序 ...

  9. [转] Hadoop MapReduce开发最佳实践(上篇)

    前言 本文是Hadoop最佳实践系列第二篇,上一篇为<Hadoop管理员的十个最佳实践>. MapRuduce开发对于大多数程序员都会觉得略显复杂,运行一个WordCount(Hadoop ...

  10. Mapreduce简要原理与实践

    探索Mapreduce简要原理与实践 目录-探索mapreduce 1.Mapreduce的模型简介与特性?Yarn的作用? 2.mapreduce的工作原理是怎样的? 3.配置Yarn与Mapred ...

随机推荐

  1. Android studio 使用技巧和问题

    最近更新Android studio版本到1.2.1.1后 出现了一些问题,首先一个就是创建一个项目后,布局文件会提示 找不到类. 网上找了下答案,原来是这个版本的bug. 其实解决起来很简单,找到 ...

  2. python基础-第八篇-8.1初识Socket

    socket基础 socket通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄,应用程序通常通过"套接字"向网络发出请求或者应答网络请求. so ...

  3. Spring Data之Hello World

    1. 概述 SpringData : 注意目标是使数据库的访问变得方便快捷;支持NoSQL和关系数据存储; 支持NoSQL存储: MongoDB(文档数据库) Neo4j(图形数据库) Redis(键 ...

  4. 剑指Offer——按之字形顺序打印二叉树

    题目描述: 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推. 分析: 我们都知道二叉树的层次遍历用的是队 ...

  5. LeetCode_Symmetric Tree

    Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). For e ...

  6. 深入浅出java IO模型

    一.同步和异步 同步:一个事件或者任务的执行,会使整个流程暂时等待,也就是说如果有多个任务要执行,必须要逐个进行. 异步:一个事件或者任务的执行,不会使整个流程暂时等待,也就是说如果有多个任务要执行, ...

  7. Android training–android studio

    又重新开始学习android开发了,希望这次不是三分钟热度.之前是利用eclipse+ADT来开发的,官网上建议用Android Studio.刚好重装了系统,升级了内存.于是下个studio来学学. ...

  8. python将图片转化为字符图

    最近看到将图片转化为字符图的小实验,我觉得很有趣,所以决定自己实现一下. 步骤和原理如下: 读取图片的灰度值矩阵(0-255之间),灰度值矩阵主要反映的是图片的黑白程度,越黑越接近与0,越白越接近于2 ...

  9. 史上最有魄力公司!20亿主要用于团队建设,要在上海做出一家BAT之外的互联网公司

    在去年的创业大军里,有一家公司显得很特别——微鲸科技,背靠华人文化,联合阿里巴巴.腾讯和央广,天使轮就高达20亿,是被誉为互联网电视领域的豪华创业团队. 在上市不到半年的时间里,旗下发布的55吋和43 ...

  10. java static成员变量方法和非static成员变量方法的区别 ( 二 )

    原创文章,未经作者允许,禁止转载!!! 静态成员变量不用new对象,在类加载的过程中就已经初始化存放在数据区域,静态成员变量是类和所有对象共有的,类和对象都可以改变它的值,每一次改变值之后,静态成员变 ...