hadoop streaming (shell执行 & combiner & 数据分割)

来源:互联网 发布:如何自学软件编程 编辑:程序博客网 时间:2024/06/05 00:29

 1,前言 --- 先上干货:hadoop.streaming 的一个完整的shell脚本;hadoop streaming (shell执行脚本实例 & combiner初探 & 数据分割)
  ### 2--- tasks 
 22 HADOOP=/usr/bin/hadoop 
 23 
 24 local_file="./wc.data"
 25 #input="yapeng/WC/input/*.txt"
 26 input="yapeng/WC/input2"
 27 output="yapeng/WC/output"
 28 
 29 $HADOOP fs -rm -r -skipTrash $input
 30 $HADOOP fs -put $local_file $input
 31 $HADOOP fs -rm -r -skipTrash $output
 32 #-inputformat com.hadoop.mapred.DeprecatedLzoTextInputFormat \
 33 #-D stream.map.input.ignoreKey='true' \
 34 $HADOOP org.apache.hadoop.streaming.HadoopStreaming \
 35 -D mapred.job.name="WC_TEST" \
 36 -D mapred.reduce.tasks=4  \
 37 -D mapred.output.compress=true \
 38 -D mapred.output.compression.type=BLOCK \
 39 -D mapred.output.compression.codec=com.hadoop.compression.lzo.LzopCodec \                                                                                                      
 40 -input $input \
 41 -output $output \
 42 -file ./interval.json \
 43 -file ./2016112917_badmid.data \
 44 -mapper "python wc_mapper.py ./2016112917_badmid.data"  -file ./wc_mapper.py \
 45 -combiner "python wc_reducer.py"  -file ./wc_reducer.py \
 46 -reducer "python wc_reducer.py"  -file ./wc_reducer.py                                                                                                                         
 47 
 48 exit -1
 49 ### 3--- judge 
 50 $HADOOP fs -test -e "$OUTPUT/_SUCCESS" 
 51 if [ $? -eq 0 ]; then
 52         echo "Succ!!!"
 53 else
 54         echo "hadoop task fail!"
 55 fi
 56 
 57 ### 4--- cp from remote to local
 58 $HADOOP fs -cat "$OUTPUT/part-*" |lzop -dc > wap_wuid_keywordtag.${DAY}

2 -- combiner初探

2.1 为什么需要进行Map规约操作

众所周知,Hadoop框架使用Mapper将数据处理成一个个的<key,value>键值对,在网络节点间对其进行整理(shuffle),然后使用Reducer处理数据并进行最终输出。



在上述过程中,我们看到至少两个 性能瓶颈 :

(1)如果我们有10亿个数据,Mapper会生成10亿个键值对在网络间进行传输,但如果我们只是对数据求最大值,那么很明显的Mapper只需要输出它所知道的最大值即可。这样做不仅可以减轻网络压力,同样也可以大幅度提高程序效率。

总结: 网络带宽严重被占降低程序效率;

(2)假设使用美国专利数据集中的国家一项来阐述 数据倾斜 这 个定义,这样的数据远远不是一致性的或者说平衡分布的,由于大多数专利的国家都属于美国,这样不仅Mapper中的键值对、中间阶段(shuffle)的 键值对等,大多数的键值对最终会聚集于一个单一的Reducer之上,压倒这个Reducer,从而大大降低程序的性能。

总结: 单一节点承载过重降低程序性能;那么,有木有一种方案能够解决这两个问题呢?

在MapReduce编程模型中,在Mapper和Reducer之间有一个非常重要的组件,它解决了上述的性能瓶颈问题,它就是 Combiner 。

PS:

与mapper和reducer不同的是,combiner没有默认的实现,需要显式的设置在conf中才有作用。

并不是所有的job都适用combiner,只有操作满足结合律的才可设置combiner。combine操作类似于:opt(opt(1, 2, 3), opt(4, 5, 6))。如果opt为求和、求最大值的话,可以使用,但是如果是求中值的话,不适用。

每一个map都可能会产生大量的 本地输出 , Combiner 的作用就是对map端的输出 先做一次合并 ,以 减少在map和reduce节点之间的数据传输量 ,以 提高网络IO性能 ,是MapReduce的一种 优化手段之一 

(1)Combiner最基本是实现 本地key的聚合,对map输出的key排序,value进行迭代 。如下所示:

map: (K1, V1) → list(K2, V2) 

combine: (K2, list(V2)) → list(K2, V2) 

reduce: (K2, list(V2)) → list(K3, V3)

(2)Combiner还有 本地reduce 功能(其本质上就是一个reduce,当然也可以完全独立的一个.py文件),例如Hadoop自带的wordcount的例子和找出value的最大值的程序,combiner和reduce完全一致,如下所示:

map: (K1, V1) → list(K2, V2) 

combine: (K2, list(V2)) → list(K3, V3) 

reduce: (K3, list(V3)) → list(K4, V4)

PS: 现在想想,如果在wordcount中不用combiner,那么所有的结果都是 reduce完成,效率会相对低下。使用combiner之后,先完成的map会在本地聚合,提升速度。对于hadoop自带的wordcount的例 子,value就是一个叠加的数字,所以map一结束就可以进行reduce的value叠加,而不必要等到所有的map结束再去进行reduce的 value叠加。

3 --- hadoop streaming 的跨语言
1), Hadoop Streaming 实战: c++编写map&reduce程序,与python写的map&reduce完全一致

2),c/cpp需要事先编译成可执行文件,-file的是-o文件,其它的例如run_hadoop.sh 的写法完全和python一致

3),cat wc.data | ./mapper  | sort | ./reducer  

4), 总之,hadoopstreaming 存在一致性, 核心是根据需求 如何书写map&red的实现,以及选择何种语言的问题  

5), 例子:mapper 是c程序, reducer是python。。  
    ##time cat mid_2_vec.all | ./cc hotword_top_kw.vec   | sort | python hw_mids_reducer.py

4, hadoop job 重要性能参数
name说明mapred.task.profile是否对任务进行profiling,调用java内置的profile功能,打出相关性能信息mapred.task.profile.{maps|reduces}对几个map或reduce进行profiling。非常影响速度,建议在小数据量上尝试mapred.job.reuse.jvm.num.tasks1表示不reuse,-1表示无限reuse,其他数值表示每个jvm reuse次数。reuse的时候,map结束时不会释放内存!mapred.{map|reduce}.tasks.speculative.execution会对运行慢的任务起一个备份任务,看哪个先完成,kill掉后完成的备份io.sort.spill.percent开始spill的内存比例阈值,对map和reduce都生效mapred.job.shuffle.input.buffer.percentreduce在copy时使用的堆空间的比例mapred.tasktracker.{map|reduce}.tasks.maximum一个Tasktracker上可同时运行的最大map、reduce任务数mapred.reduce.copy.backoffreduce获取一份map输出数据的最大时间,单位秒。io.compression.codecs压缩算法dfs.block.sizehdfs上的文件block大小mapred.reduce.slowstart.completed.maps控制reduce的启动时机。表示全部map的百分之多少完成后,才启动reduce。如果机器内存紧张,可以适当设大改参数,等大部分map结束并释放内存后才启动reduce;如果希望尽快开始shuffle,则可配合大量map数,将该值设小,以尽早启动reduce,开始copy。io.sort.mbmap使用的缓存,影响spill的次数。mapred.child.java.opts同时设置map和reduce的jvm参数mapred.map.child.java.opts分开设置map的jvm参数,包括GC策略mapred.reduce.child.java.opts分开设置reduce的jvm参数map.sort.class对map的输出key的排序方法mapred.output.compression.type压缩类型mapred.min.split.size每个map的最小输入大小,该值越大,map数越少mapred.max.split.size每个map的最大输入大小,该值约小,map数越多mapred.reduce.parallel.copiesreduce从map结果copy数据时,每个reduce起的并行copy线程数。该值越大,io压力越大,但可能引起网络堵塞,copy效率反而降低。io.sort.factormerge时的并行merge数,同时影响map的spill文件merge和reduce中的mergemapred.compress.map.output指定map的输出是否压缩。有助于减小数据量,减小io压力,但压缩和解压有cpu成本,需要慎重选择压缩算法。mapred.map.output.compression.codecmap输出的压缩算法mapred.output.compressreduce输出是否压缩mapred.output.compression.codec控制mapred的输出的压缩的方式io.sort.record.percentmap中间数据的index和data在io.sort.mb中占内存的比例,默认0.05%,需要根据具体数据的特点调整:index的大小固定为16byte,需要根据data的大小调整这个比例,以使io.sort.mb的内存得到充分利用)。

mapred.reduce.slowstart.completed.maps (map完成多少百分比时,开始shuffle)

当map运行慢,reduce运行很快时,如果不设置mapred.reduce.slowstart.completed.maps会使job的shuffle时间变的很长,

map运行完很早就开始了reduce,导致reduce的slot一直处于被占用状态。mapred.reduce.slowstart.completed.maps 这个值是

和“运行完的map数除以总map数”做判断的,当后者大于等于设定的值时,开始reduce的shuffle。所以当map比reduce的执行

时间多很多时,可以调整这个值(0.75,0.80,0.85及以上)

mapred.min.split.size

这个配置决定了每个Input Split 的最小值,也间接决定了一个job的map数量

HDFS块大小是在job写入时决定的,而分片的大小,是由三个元素决定的(在3各种去最大的那个)

(1) 输入的块数 (2) Mapred.min.split.size (3) Job.setNumMapTasks()

mapred.compress.map.output

压缩Map的输出,这样做有两个好处:
a)压缩是在内存中进行,所以写入map本地磁盘的数据就会变小,大大减少了本地IO次数
b) Reduce从每个map节点copy数据,也会明显降低网络传输的时间
注:数据序列化其实效果会更好,无论是磁盘IO还是数据大小,都会明显的降低。

        -D mapred.reduce.slowstart.completed.maps=1 \
        -D stream.map.input.ignoreKey='true' \
        -D mapred.job.istunneljob=true \
        -D mapred.job.name=$JOBNAME \
        -D mapred.reduce.tasks=$REDUCENUM \
        -D mapred.output.compress=true \
    -D mapred.output.compression.type=BLOCK \
    -D mapred.output.compression.codec=com.hadoop.compression.lzo.LzopCodec \
    -inputformat com.hadoop.mapred.DeprecatedLzoTextInputFormat \

Hadoop Streaming框架使用(三)

  前两篇文章介绍了Hadoop Streaming框架的使用方法。由于篇幅所限,并没有介绍其中的高级使用方法,但是有一些用法还是相当常见的。今天对一些高级用法进行一个简单的说明,希望能给大家一些启发。

  1 使用cacheFile分发文件

  如果文件(如字典文件)存放在HDFS中,希望计算时在每个计算节点上将文件当作本地文件处理,,可以使用-cacheFile hdfs://host:port/path/to/file#linkname选项在计算节点缓存文件,Streaming程序通过./linkname访问文件。

  例如:

   hadoop = `which hadoop`

   $hadoop streaming \

   -input /user/test/input -output /user/test/output \

-mapper mymapper.sh -reducer myreducer.sh \

   -file /home/work/mymapper.sh \ 

-file /home/work/myreducer.sh \

-cacheFile hdfs://namenode:port/user/test/dict.data#dictlink \

-jobconf mapred.job.name=”cache-file-demo”

  mymapper.sh和myreducer.sh可以通过./dictlink直接访问字典文件hdfs://user/test/dict.data,而且是从本地读取文件。

 

  2 用cacheArchive分发压缩包

  有时要分发的文件有一定的目录结构,可以先将整个目录打包,然后整体进行上传。使用-cacheArchive hdfs://host:port/path/to/archivefile#linkname分发压缩包。

例如在本地有一个目录为app,里面有mapper.pl, reducer.pl, dict/dict.txt这些子目录和文件,mapper.pl和reducer.pl要读取./dict/dict.txt文件,希望在任务执行时不需要修改程序和目录结构, 可以按照下面的方式分发app目录:

   $ tar app.tar.gz –C app .  #本地打包

   $ $HADOOP_HOME/bin/hadoop fs –put app.tar.gz /user/test/app.tar.gz   #包上传到HDFS

$ $HADOOP_HOME/bin/hadoop streaming \

-input /user/test/input -output /user/test/output \

-mapper “perl app/mapper.pl” -reducer “perl app/reducer.pl” \

-cacheArchive hdfs://namenode:port/user/test/ app.tar.gz #app \

-jobconf mapred.job.name=”cache-archive-demo”

首先将本地app目录中的所有文件和目录打包压缩,然后上传到HDFS的/user/test/app.tar.gz,启动streaming任务时使用-cacheArchive选项将app.tar.gz分发到计算节点并解压到app目录,然后在当前工作目录创建到app目录的链接,-mapper选项指定app/mapper.pl为mapper程序,-reducer选项指定app/reducer.pl为reducer程序,它们都可以读取./dict/dict.txt文件。本地打包时要进入目录app而不是在app的上层目录打包,否则要通过app/app/mapper.pl才能访问到mapper.pl文件。

hadoop支持zip, jar, tar.gz格式的压缩包,由于Java解压zip压缩包时会丢失文件权限信息而且遇到中文文件名会出错,所见建议采用tar.gz压缩包。

三种文件分发方式的区别:-file将客户端本地文件打成jar包上传到HDFS然后分发到计算节点,-cacheFile将HDFS文件分发到计算节点,-cacheArchive将HDFS压缩文件分发到计算节点并解压。


3输出数据分割

默认情况下Streaming框架将map输出的每一行第一个”\t”之前的部分作为key,之后的部分作为value,key\tvalue又作为reduce的输入。可以用-D stream.map.output.field.separator改变map输出中key和value的分隔符,用-D stream.num.map.output.key.fields设置分隔符的位置,该位置之前的部分作为key,之后的部分作为value。如下所示,其中-D stream.map. output.field.separator=:指定使用冒号”:”将map输出的一行分隔为key/value,-D stream.num.map.output.key.fields=2指定在第二个冒号处进行分隔,也就是第二个冒号之前的作为key,之后的作为value。如果没有冒号或冒号少于两个,则key为整行,value为空。 

   $HADOOP_HOME/bin/hadoop streaming \

       -D stream.map.output.field.separator=: \

       -D stream.num.map.output.key.fields=2 \

-input /user/test/input -output /user/test/output \

-mapper mymapper.sh -reducer myreducer.sh \

-file /home/work/mymapper.sh \

-file /home/work/myreducer.sh \

-jobconf mapred.job.name=”output-sep-demo”

与map类似,对于reduce的输出,同样也可以用-D stream.reduce.output.field.separator-D stream.num.reduce.output.key.fields定制key/value分隔方式。


4 二次排序

  KeyFieldBasedPartitioner是Hadoop库中的一个实用Partitioner,配置相应的参数就可以使用,通过KeyFieldBasedPartitioner可以方便地实现二次排序。 

   $HADOOP_HOME/bin/hadoop streaming \

       -D stream.map.output.field.separator=. \

       -D stream.num.map.output.key.fields=4 \

       -D map.output.key.field.separator=. \

       -D num.key.fields.for.partition=2 \

   -input /user/test/input -output /user/test/output \

   -mapper “mymapper.sh” -reducer “ myreducer.sh” \

-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \

-file /home/work/mymapper.sh \

-file /home/work/myreducer.sh \

-jobconf mapred.job.name=”key-partition-demo”

  其中-Dstream.map.output.field.separator=.-D stream.num.map.output.key.fields=4与上面的定制输出数据分隔方式意义相同,指定map的输出行第4个英文句号”.”之前为key,后面为value。-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner指定使用KeyFieldBasedPartitioner,-D map.output.key.field.separator=.指定key的内部用英文句号”.”分隔,-D num.key.fields.for.partition=2指定将key分隔出来的前两个部分而不是整个key用于Partitioner做partition。

  以上就是我个人认为hadoop streaming中比较常用的技巧,希望对大家有所帮助,同时也多多补充。

原创粉丝点击