MapReduce User Manual

来源:互联网 发布:手机伴奏消音软件 编辑:程序博客网 时间:2024/06/06 15:04

目录

 [隐藏] 
  • 1 MapReduce简介
  • 2 MapReduce使用示例
    • 2.1 词频统计(WordCount)--java版本
    • 2.2 词频统计(WordCount)--streaming版本
    • 2.3 查看Job运行状态
  • 3 MapReduce编程接口
    • 3.1 Java API
    • 3.2 Streaming 接口
  • 4 MapReduce平台使用规范
    • 4.1 需要使用个人用户访问集群
    • 4.2 Job的命名规范
    • 4.3 平台资源限制
    • 4.4 失败job的处理
  • 5 MapReduce程序开发常见问题
    • 5.1 hdfs操作查看
      • 5.1.1 如何跨集群访问hdfs?
      • 5.1.2 如何跨集群访问归档(.har)文件?
    • 5.2 Job提交参数
      • 5.2.1 提交任务时如何上传文件?
      • 5.2.2 如何在HDFS上缓存大文件?(DistributedCache)
        • 5.2.2.1 Streaming如何使用-cacheFile/-cacheArchive上传大文件或部署所需的软件环境?
        • 5.2.2.2 Java如何用distributedCache缓存大文件?
      • 5.2.3 提交任务时使用-D ,-files,-libjars参数不起作用或不能正确提交?
      • 5.2.4 提交job时如何指定优先使用用户的jar包?
      • 5.2.5 MapReduce中如何使用第三方jar包?
      • 5.2.6 指定参数时使用-D和-jobconf的区别?
    • 5.3 input输入相关的参数及功能
      • 5.3.1 如何指定输入路径?
      • 5.3.2 InputFormat
        • 5.3.2.1 TextInputFormat(默认的inputFormat)
        • 5.3.2.2 CombineTextInputFormat(输入是小文件时使用)
        • 5.3.2.3 使用CombineTextInputFormat后,怎么处理不同类型的文件?
      • 5.3.3 MapReduce怎么处理不同类型的输入文件?(MultipleInputs)
    • 5.4 output输出相关的参数及功能
      • 5.4.1 如何对输出进行压缩?
      • 5.4.2 OutputFormat
        • 5.4.2.1 TextOutputFormat
        • 5.4.2.2 如何将MapReduce的一个输出写入多个文件?(TextMultiOutputFormat)
        • 5.4.2.3 MapReduce如何使用多路输出?
    • 5.5 Map/Reduce执行相关参数功能
      • 5.5.1 Mapper
        • 5.5.1.1 如何设置map的输出key/value类型?
        • 5.5.1.2 如何在map或reduce中获得唯一id号?
      • 5.5.2 Partitioner
        • 5.5.2.1 如何使用自定义的partitioner?
      • 5.5.3 Comparator
        • 5.5.3.1 默认的Comparator
        • 5.5.3.2 KeyFieldBasedComparator(非字节排序,如按照数字排序)
        • 5.5.3.3 排序加速
      • 5.5.4 Combiner
        • 5.5.4.1 Map端如何使用Combiner?
        • 5.5.4.2 常用的Combiner示例
      • 5.5.5 Reducer
        • 5.5.5.1 如何将Counters写入_SUCCESS文件?
        • 5.5.5.2 如何设置MapReduce的语言环境?
      • 5.5.6 MR TIPS
        • 5.5.6.1 如何自定义计数器和进行状态汇报?
        • 5.5.6.2 Streaming程序当map或reduce返回值非0时,整个任务会失败?
        • 5.5.6.3 Streaming任务如何指定key,value的分隔符?
        • 5.5.6.4 Streaming程序如何判断管道的每个进程返回值?
    • 5.6 Job调度参数功能
      • 5.6.1 如何设置JobName?
      • 5.6.2 如何设置Reduce的个数?
      • 5.6.3 如何限制Job的task并发数?
      • 5.6.4 如何设置Job的失败比例?
      • 5.6.5 如何设置Job的优先级?
      • 5.6.6 如何设置task的超时时间?
      • 5.6.7 如何打开/关闭预测执行?
      • 5.6.8 如何设置slow start?
  • 6 MapReducejob常见问题及解决方法
    • 6.1 如何找到失败task的对应输入?
    • 6.2 如何判断job的运行结果?
    • 6.3 如何处理syslog输出超限的错误?
    • 6.4 其他
  • 7 如何自主部署hadoop不支持的运行环境
    • 7.1 php的mcrypt扩展示例
  • 8 附录
    • 8.1 MapReduce常见参数表
    • 8.2 MultiOutputFormat实例
    • 8.3 MultiOutputs实例
    • 8.4 hhvm实例
    • 8.5 pypy实例

MapReduce简介

MapRduce采用“分而治之”的思想,把对大规模数据集的操作,分发给一个主节点管理下的各分节点,使其协作共同完成计算任务,然后再通过整合各个分节点的中间结果,得到最终的结果。简而言之,MapReduce就是“任务的分解与计算结果的整合”。

对于这个处理过程,我们将MapReduce高度抽象为两个函数,即mapreduce:

  • map负责任务分解
  • reduce负责将分解后的多个任务处理的结果整合汇总在一起

MapReduce的特点

  • 编程简单,基本的只需实现map和reduce函数。
程序员无需担心并行编程中的其他复杂问题,比如,分布式存储、工作调度、负载均衡、容错处理、网络通信等
  • 采用MapReduce处理的数据集或任务,必须有以下特性:待处理的数据集或任务可以被分解成多个小的子集,并且每个子集可以完全并行的进行处理,而互不影响
实际应用中,有很多典型的应用,包括:URL访问频率统计、分布式grep、倒排索引构建、分布式排序等


MapReduce使用示例

本节提供java和streaming方式的典型应用:词频统计,其中streaming支持shell、php、python、perl、C等语言。

词频统计(WordCount)--java版本

示例代码

   import java.io.IOException;   import java.util.StringTokenizer;      import org.apache.hadoop.conf.Configuration;   import org.apache.hadoop.conf.Configured;   import org.apache.hadoop.fs.Path;   import org.apache.hadoop.io.IntWritable;   import org.apache.hadoop.io.LongWritable;   import org.apache.hadoop.io.Text;   import org.apache.hadoop.mapreduce.lib.input.*;   import org.apache.hadoop.mapreduce.lib.output.*;   import org.apache.hadoop.mapreduce.Job;   import org.apache.hadoop.mapreduce.Mapper;   import org.apache.hadoop.mapreduce.Reducer;   import org.apache.hadoop.util.Tool;   import org.apache.hadoop.util.ToolRunner; 
public class WordCount extends Configured implements Tool { public static class MyMap extends Mapper<LongWritable, Text, Text, IntWritable> { private final static IntWritable one = new IntWritable(1); private Text word = new Text(); // map函数key:字符串偏移量,LongWritable类型;value:一行字符串内容,Text类型 @Override public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException { String line = value.toString(); StringTokenizer tokenizer = new StringTokenizer(line); //将字符串分割成单词 while (tokenizer.hasMoreTokens()) { word.set(tokenizer.nextToken()); context.write(word, one); //迭代输出key/value对 } } } public static class Reduce extends Reducer<Text, IntWritable, Text, IntWritable> { // reduce函数key:一个单词,Text类型;value:该单词出现的次数列表 @Override public void reduce(Text key, Iterable<IntWritable> values, Context context) throws IOException, InterruptedException { int sum = 0; for (IntWritable val : values) { sum += val.get(); } context.write(key, new IntWritable(sum)); } } public int run(String[] args) throws Exception { if (args.length != 2) { System.out .println("Usage:hadoop jar WordCount.jar WordCount <input> <output> "); System.exit(-1); } // 必要的配置参数 Configuration conf = getConf(); Job job = new Job(conf); job.setJarByClass(WordCount.class); // 设置主类 job.setJobName("wuyunyun_WordCount"); // 设置JobName job.setMapOutputKeyClass(Text.class); // 设置Job的Map输出key类型(如需指定map的输出Key类型,可通过此参数设置) job.setMapOutputValueClass(IntWritable.class); // 设置Job的Map输出value类型(如需指定map的输出Value类型,可通过此参数设置) job.setOutputKeyClass(Text.class); // 设置Job的输出key类型 job.setOutputValueClass(IntWritable.class); // 设置Job的输出value类型 job.setMapperClass(MyMap.class); // 设置Mapper类 job.setReducerClass(Reduce.class); // 设置Reducer类 job.setNumReduceTasks(1); // 设置Reduce task的个数 job.setInputFormatClass(TextInputFormat.class); // 设置Job输入分割格式(InputFormat) job.setOutputFormatClass(TextOutputFormat.class);// 设置Job的输出格式(OutputFormat) //从命令行参数中获取输入输出路径 FileInputFormat.setInputPaths(job, new Path(args[0])); FileOutputFormat.setOutputPath(job, new Path(args[1])); boolean success = job.waitForCompletion(true); return success ? 0 : 1; }

public static void main(String[] args) throws Exception { int ret = ToolRunner.run(new WordCount(), args); System.exit(ret); } }

编译测试提交Job

  • 编译并打包
javac -d classes WordCount.javajar cvf WordCount.jar -C classes/ .
  • 提交Job
hadoop jar WordCount.jar WordCount <input_path> <output_path>例如:hadoop jar WordCount.jar WordCount /home/mr/wuyunyun/testfile /home/mr/wuyunyun/output1

测试数据:

[mr@w-hdp905 wordCount]$ cat testfile hello worldhello wuyunhello kittykitty wuyunyun

测试结果:

[mr@w-hdp905 wordCount]$ ~/software/hadoop/bin/hadoop fs -cat /home/mr/wuyunyun/output1/part-00000hello3kitty2world1wuyun1wuyunyun1

词频统计(WordCount)--streaming版本

示例代码

  • map.php
<?php   $in=fopen("php://stdin","r");  $out_array=array();  while ($line=fgets($in)){      $wordlist=str_word_count($line,1);      foreach($wordlist as $word){           print "$word\t1\n";      }  }  fclose($in);?>
  • reduce.php
<?php   $in=fopen("php://stdin","r");  $res_array=array();  while ($line=fgets($in)){      list($word,$count)=split("\t",$line);      if($word!="")      {          $key=$word;          $res_array[$key] +=$count;   # 对相同的key值进行累加       }   }   fclose($in);  foreach ($res_array as $key => $value)  {      print "$key\t$value\n";          # 以\t分隔key和value  }?>

本地测试程序

测试map程序:[mr@w-m2 ~]$ export LC_ALL=C[mr@w-m2 ~]$ cat testfile | php map.php | sort > map.out测试reduce程序:[mr@w-m2 ~]$ cat map.out | php reduce.php

提交job

  • job提交脚本--run.sh
  hadoop streaming \  -input /home/mr/wuyunyun/testfile \  -output /home/mr/wuyunyun/output \  -mapper "php map.php" \  -reducer "php reduce.php" \  -jobconf mapred.reduce.tasks=1 \  -file ./map.php \  -file ./reduce.php \  -jobconf mapred.job.name=wuyunyun_wordcount
注: 
1. "\"是换行,换行符前务必只保留一个空格
2.可以使用-D 或者 -jobconf指定需要功能参数,但使用-D时,只能将其放置在所有参数的最前面,而-jobconf可以放置在任何位置
约定:为了更好的管理mapreduce平台上的job,请遵守job命名规范: 提交者域账户_具有实际意义的job名称,中间用下划线分割。 如邮箱名为:zhangsan-sal@360.cn,请命名为:zhangsan-sal_wordcount 如果您的程序无法在本地进行调试,进行线上测试时,请在jobName后添加后缀"_test",这些测试的失败邮件将只会发给提交人,而不会发给接口人。

查看Job运行状态

任务提交成功后,job运行进度会显示在客户端的标准输出中:
Job运行时.jpg

注:提交在集群上的job,kill后请务必确认kill成功,以免浪费集群资源

确认kill job操作成功的方法:

  1. 查看kill命令的返回值,为0,则kill成功:echo $?
  2. 在JobTracker作业监控页面,Failed Job列表中看到此job,且状态为Killed

MapReduce编程接口

Java API

用户编写MapReduce需要实现的类或方法有:

1.Mapper接口:

用户需继承Mapper接口实现自己的Mapper,其中包含的方法有:
public class Mapper<KEYIN, VALUEIN, KEYOUT, VALUEOUT> {需要实现的方法:   //每一个key/value对均会调用一次map方法,默认的map只输出key/value,不进行任何处理。用户需要自己实现map方法,对输入的key/value对进行逻辑处理。  void map(KEYIN key, VALUEIN value, Context context) throws IOException, InterruptedException {   //Context为上下文环境对象,用户可通过此对象将输出的key/value写入输出流中、获取配置文件信息、progress信息等;     context.write((KEYOUT) key, (VALUEOUT) value);   }可选择实现的方法:  void setup(Context context) throws IOException, InterruptedException{  // 用户可根据需求实现task的初始化工作(如:打开文件),默认不做任何操作,在task开始前调用一次     // Nothing   }  void cleanup(Context context) throws IOException, InterruptedException{  // 用户可根据需求实现 task的收尾清理工作(如:关闭文件),默认不做任何操作,在task结束后调用一次    //Nothing   }}

2.Reducer接口:

用户需继承Reducer接口实现自己的Reducer,其中包含的方法有:
public class Reducer<KEYIN,VALUEIN,KEYOUT,VALUEOUT> {需要实现的方法:// 对每一个key均会调用一次reduce方法,默认的reduce只输出key/value,不进行任何处理  void reduce(KEYIN key, VALUEIN value, Context context) throws IOException, InterruptedException {      for(VALUEIN value: values) {       context.write((KEYOUT) key, (VALUEOUT) value);      }    }可选择实现的方法:(同Mapper)  void setup(Context context) throws IOException, InterruptedException {       //Nothing    }  void cleanup(Context context) throws IOException, InterruptedException {       //Nothing    }}

函数中的参数<KEYOUT, VALUEOUT>类型通过setOutputKeyClass()及setOutputValueClass()设置,如示例WordCount的conf配置。

代码片段:Job.setOuputKeyClass(Text.class);Job.setOutputValueClass(IntWritable.class);

注:Reducer接口中的KEYIN,VALUEIN必须和Mapper中的KEYOUT,VALUEOUT类型一致

3.Main函数
建议用户在在main函数中实现Tool接口,通过ToolRunner来运行程序,这样就可以解析通过命令行传递的MapReduce参数。(ToolRunner内部调用GenericOptionParser可以对命令行参数进行解析)
例如:

 public int run(String[] args) throws Exception {        if (args.length != 2) {            System.out                 .println("Usage:hadoop jar WordCount.jar WordCount <input> <output> ");           System.exit(-1);        }        // 必要的配置参数        Configuration conf = getConf();        Job job = new Job(conf);        job.setJarByClass(WordCount.class);              // 设置主类        job.setJobName("wuyunyun_WordCount");            // 设置JobName        job.setOutputKeyClass(Text.class);               // 设置Job的输出key类型        job.setOutputValueClass(IntWritable.class);      // 设置Job的输出value类型        job.setMapperClass(MyMap.class);                 // 设置Mapper类        job.setReducerClass(Reduce.class);               // 设置Reducer类        job.setNumReduceTasks(1);                        // 设置Reduce task的个数        job.setInputFormatClass(TextInputFormat.class);  // 设置Job输入分割格式(InputFormat)        job.setOutputFormatClass(TextOutputFormat.class);// 设置Job的输出格式(OutputFormat)           //从命令行参数中获取输入输出路径        FileInputFormat.setInputPaths(job, new Path(args[0]));        FileOutputFormat.setOutputPath(job, new Path(args[1]));          boolean success = job.waitForCompletion(true);        return success ? 0 : 1;    }
public static void main(String[] args) throws Exception { int ret = ToolRunner.run(new WordCount(), args); System.exit(ret); }

Streaming 接口

Hadoop MapReduce和HDFS采用Java实现,默认提供Java编程接口,另外提供了C++编程接口和Streaming框架。Streaming框架允许任何程序语言实现的程序在Hadoop MapReduce中使用,方便原有程序向Hadoop平台移植

1. Mapper程序

用户程序实现的mapper程序需要将从标准输出中获得的每一行内容转换成key/value对(“key \t value”)作为mapper的输出。

默认情况下,一行中第一个tab之前的部分作为key,之后的(不包括tab)作为value。如果没有tab,整行作为key值,value值为null。(Reducer同理)
程序返回值:默认情况,streaming task返回非零时表示失败,所以grep示例中我们需要设置exit 0,以避免grep查找不到时程序返回非零值 link pipe
注:用户可根据需求直接在脚本中获取一些常用的环境变量,进行判断和逻辑处理。


2. Reducer程序

用户的实现的reducer程序从标准输出获取输入的key/value对,经过用户逻辑处理后得到聚合的key/value对,作为reduer的输出即MapReduce的最终输出,写至HDFS上。

3.streaming程序返回值 对于Streaming程序,MR框架会判断map和reduce的返回值,如果返回值非0,则认为map或reduce执行中出现错误,重试数次(默认为4)后仍然失败,会导致整个任务失败

4.提交job:
1) 指定附加配置参数:

用户可以使用“-jobconf <name>=<value>”增加一些配置变量。

如我们指定jobName,可以使用“-jobconf mapred.job.name=xxx_wordCount”
指定reduce task个数,可以使用“-jobconf mapre.reduce.tasks=1”

注:使用-D和-jobconf指定配置参数的区别:-D是通用的,-jobconf 只适用于streaming

2)将文件打包到提交的作业中:

任何可执行文件都可以被指定为mapper/reducer。这些可执行文件不需要事先存放在集群上; 如果在集群上还没有,则需要用-file选项让MapReduce框架把可执行文件作为作业的一部分,一起打包提交。如streaming方式提交job

3) 上传大文件和压缩文件

有时我们需要上传一些大文件或压缩词典文件,请使用-cacheFile和-cacheArchive选项在集群中分发文件。

具体方法请见:使用-cacheFile/cacheArchive上传大文件至HDFS

4) 只使用Mapper的作业:

有时只需要map函数处理输入数据。这时只需把mapred.reduce.tasks设置为零,MapReduce框架就不会创建reducer任务,mapper任务的输出就是整个作业的最终输出。

使用-reducer NONE 或 -jobconf mapred.reduce.tasks=0 选项。

5)为作业指定其他插件:

和其他普通的Map/Reduce作业一样,用户可以为streaming作业指定其他插件:
 -inputformat <JavaClassName>     //例如:-inputformat org.apache.hadoop.mapred.lib.CombineTextInputFormat -outputformat <JavaClassName>    //例如:-outputformat org.apache.hadoop.mapred.TextMultiOutputFormat -partitioner <JavaClassName>     //例如:-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner -combiner <JavaClassName/可执行程序>   //例如:-combiner "sh myCombiner.sh"

如果不指定inputformat,则默认会使用TextInputFormat。

6) 集群中的软件环境版本

这里主要介绍当前集群MR软件版本:
  • Hadoop版本:hadoop-0.20.1
  • Perl版本:5.10.1
  • PHP版本:
/usr/bin/php 版本为:5.1.6/usr/local/bin/php 版本为:5.2.10  (建议使用此版本,其中包括常用的php扩展包)/usr/local/php7/bin/php 版本为:7.0.0 RC 3(safe.lycc,dfs.shjt) 7.0.0 Beta 3(qss.zzbc2) (php7对原有php兼容性能肯定比hhvm要好,所以后续hadoop php性能优化会主推php7,欢迎大家试用、提出建议。)hhvm-2.3.2,此版本性能比原生php性能好,如果没有使用特殊的php扩展,强烈推荐使用这个版本提高性能,参见-cacheArchive的方式部署软件环境
  • Python版本:
/usr/bin/python 版本2.4.3  (默认)/usr/bin/python2.6 版本为2.6.5 pypy 高性能的python版本(由hadoop粉丝 范晓恺 <fanxiaokai@360.cn>提供)

如不能满足应用需求,请自行上传软件环境, 参见-cacheArchive的方式部署软件环境

MapReduce平台使用规范

需要使用个人用户访问集群

  1. HDFS目录访问:请先发邮件到g-xitong-hadoop邮件组,标明申请开通的对应组、个人域账号以及访问目录。待接口人审批后,则可以以个人账号访问目录。
  2. MR用户组提交:请先发邮件到g-xitong-hadoop邮件组,申请开通个人权限。然后在提交作业时,使用-jobconf mapred.fairscheduler.pool=,指定job需要提交到的组pool。

Job的命名规范

为了更好的管理mapreduce平台上的job,请遵守如下job命名规范:
mapred.job.name=提交者邮箱域账户_具有实际意义的job名称,中间请用下划线分割
如邮箱名为:zhangsan-sal@360.cn,请命名为:zhangsan-sal_wordcount
无法进行本地调试的job,请在命名后添加“_test”标注

平台资源限制

  1. 单个map/reduce task内存限制为1G
  2. Map task总个数不能超过20万
  3. Job的jar包不超过100M
  4. 磁盘限制:
  • 每个map的输出不超过20G
  • Task log不超过1G


失败job的处理

对于提交至集群上的Job,我们会进行失败Job监控,同时发送失败job的邮件至job提交者。请job提交者规范job命名,否则我们的监控脚本会因无法匹配job提交者,而将失败邮件推送给job提交者所在的hadoop组所有成员。

为避免其他同事受此干扰,请您按照我们的约定对Job命名进行严格规范,谢谢。

失败邮件页面如下:
失败job邮件页面.jpg

MapReduce程序开发常见问题

hdfs操作查看

如何跨集群访问hdfs?

  命令参数形式   -fs <local|namenode:port>  使用范例       ./hadoop fs -ls hdfs://w-namenode.dfs.shgt.qihoo.net:9000/home  常用集群namenode      safe_lycc  hdfs://namenode.safe.lycc.qihoo.net:9000       qss_zzbc2  hdfs://w-namenode.qss.zzbc2.qihoo.net:9000      dfs_shjt   hdfs://w-namenode.dfs.shgt.qihoo.net:9000    TIP:报attempt错误,请到云图为相关客户机申请集群访问加白。

如何跨集群访问归档(.har)文件?

  使用范例       1)查找归档文件           ./hadoop fs -ls hdfs://w-namenode.dfs.shgt.qihoo.net:9000/home/mr/example.har                 2)查看归档文件里的数据   ./hadoop fs -ls har://hdfs-w-namenode.dfs.shgt.qihoo.net:9000/home/mr/example.har                 3)查看归档文件里的子目录 ./hadoop fs -ls har://hdfs-w-namenode.dfs.shgt.qihoo.net:9000/home/mr/example.har/SubDir

Job提交参数

提交任务时如何上传文件?

上传本地文件:  -file         #上传用户的本地脚本文件(1个)java程序请使用-files  -libjars      #上传用户指定的jar包并加入classpath中(多个jar包以逗号分隔)    -cacheFile    #指定使用缓存在hdfs上的大文件  -cacheArchive #指定使用缓存在hdfs上的压缩文件注:
1)-libjars参数需放置在java 主类名之后,作为第一个指定参数
2)以上指定参数上传的文件或jar包均会打入job的jar包中,并在map task初始化时分发到各个节点上,如果job.jar包过大(>100M),加之map并发过大,则可能会导致task初始化时超时而失败,建议大的文件使用-cacheArchive/-cacheFile上传

如何在HDFS上缓存大文件?(DistributedCache)

distributedCache可以把HDFS上的文件(数据文件、压缩文件等等)分发到各个执行task的节点。执行map或者reduce task的节点就可以在本地,直接用java的IO接口读取这些文件。

有两个需要注意的地方:被分发的文件需要事先存储在hdfs上;这些文件是只读的。 下面分别介绍Streaming和Java方式如何使用。

Streaming如何使用-cacheFile/-cacheArchive上传大文件或部署所需的软件环境?

-cacheFile: 用来上传大文件
-cacheArchive:用来上传大的压缩文件

此处我们提供一个实例: 使用-cacheArchive的方式上传data.tar.gz 具体步骤如下:

1. 将依赖的数据文件上传到hdfs某个目录:
例如:hadoop fs -put data.tar.gz hdfs:///user/cache-test/data.tar.gz 
2.提交job,采用cacheArchive方式使用数据文件。
例如:提交job的命令行参数中添加 -cacheArchive hdfs:///user/cache-test/data.tar.gz#test
在上面的例子里,url中#后的部分是建立在任务当前工作目录下的符号链接的名字,此处为test,test指向data解压后的目录。

-cacheArchive自动将压缩文件data进行解压,用户不需再做解压操作,只需在程序中采用相对路径引用即可。
注:-cacheArchive支持的是归档压缩文件(zip、tar、tgz、tar.gz)和jar文件解压

-cacheFile使用方法类似:

-cacheFile hdfs:///user/cache-test/testfile.txt#test这里的任务的当前工作目录下有一个“test”符号链接,它指向testfile.txt文件在本地的拷贝。如果有多个文件,选项可以写成:-cacheFile hdfs:///user/cache-test/testfile1.txt#test1 -cacheFile hdfs:///user/cache-test/testfile2.txt#test2

注:与-cacheArchive不同,cacheFile不对文件解压。

-cacheArchive的方式部署软件环境
以使用python2.7.3为例,具体使用方式如下:

 上传python2.7包至hdfs指定目录:      hadoop fs -put ./python2.7.tgz hdfs:///home/mr/lib/ 指定cacheArchive:                    -cacheArchive hdfs:///home/mr/lib/python2.7.tgz#python      指定map或者reduce使用的python位置:  python/bin/python 实例;     hadoop streaming \     -input /home/xitong/tmp/test/data \     -output /home/xitong/tmp/test/output/out3 \     -mapper "./python/bin/python test.py" \     -reducer "cat" \     -file test.py \     -cacheArchive hdfs:///home/mr/lib/python2.7.tgz#python


注:部署的软件环境,若是使用了#设置,请在原本的压缩包结构上再添加设置的目录名,以防止运行找不到环境。

例如:使用-cacheArchive python2.7.tgz#python,请先确认压缩包的解压的目录,然后在解压目录前添加#标识的软链名,如下:

python2.7.tgz解压后的目录为./lib与./bin……,则在程序中使用的运行环境目录为./python/lib与./python/bin……

Java如何用distributedCache缓存大文件?

使用distributedCache的步骤:

  1. 在conf里正确配置被分发的文件的路径(hdfs上的路径)
  2. 在自定义的mapper或reducer中获取文件下载到本地后的路径(linux文件系统路径);一般是重写configure(老API)或者重写setup(新API)
  3. 在自定义的mapper或reducer类中读取这些文件的内容

distributedCache也提供创建符号链接的功能,第2步就不需要获取文件在本地的路径,直接使用约定的符号链接即可。

示例:

  • 将需要依赖的文件或者归档压缩包上传至HDFS上:
hadoop fs -put data /home/mr/test/distributedcache/hadoop fs -put mydata.tgz /home/mr/test/distributedcache/
  • 在Java main函数中添加DistributedCache(old API):
JobConf job = new JobConf();DistributedCache.addCacheFile(new URI("hdfs:///home/mr/test/distributedcache/data#data"), job);    // "#"后的data表示文件data的软连接名DistributedCache.addCacheArchive(new URI("hdfs:///home/mr/test/distributedcache/mydata.tgz#mydata", job);
  • 在Mapper或者Reducer中使用cached files:
public static class MapClass extends MapReduceBase implements Mapper<K, V, K, V> {  private Path[] localArchives;  private Path[] localFiles;
@Override public void configure(JobConf job) { // Get the cached archives/files localArchives = DistributedCache.getLocalCacheArchives(job); localFiles = DistributedCache.getLocalCacheFiles(job); } @Override public void map(K key, V value, OutputCollector<K, V> output, Reporter reporter) throws IOException { // Use data from the cached archives/files here // ... // ... output.collect(k, v); }}

注:如果cache文件有变动或者更新,需要重新将其上传至HDFS上

提交任务时使用-D ,-files,-libjars参数不起作用或不能正确提交?

正确使用的方法需要满足两个条件:
程序实现:
使用GenericOptionParser解析hadoop命令行。
需要实现Tool接口,通过ToolRunner来运行程序。(ToolRunner内部调用GenericOptionParser对命令行参数进行解析)
提交任务时:
提交任务时,需要将上述参数放在jar的主类后面,紧跟主类名,否则不会解析成功。
注:streaming支持上述参数, java上传本地文件的参数是-files,streaming同时支持-file和-files。

提交job时如何指定优先使用用户的jar包?

请设置:mapreduce.user.classpath.first为true, 默认为false

MapReduce中如何使用第三方jar包?

有以下2种常用方式使用第三方jar包:
1.Streaming程序使用-libjars参数指定本地jar包路径;
2.Java方式借助DistributedCache.addFileToClassPath机制(对于使用java security相关包,必须采用这种方式,如果要用libjars和tmpjars方式,需要在本地将文件解开,再上传到hdfs上,违反了security的特性)

示例:DistributedCache.addFileToClassPath(new Path("/home/mr/libjars/bcprov-ext-jdk15on-148.jar"),conf);

指定参数时使用-D和-jobconf的区别?

在提交streaming程序时 -jobconf和-D 均可以指定提交功能参数,但使用-D 时必须将次参数放置在所有参数之首;在提交java中只能使用-D
P.S. 此处可能有人会容易记混 -jobconf 和 -D, 提供一个小窍门, 命令行中直接运行 "hadoop streaming" 回车会得到如下提示: [spider@client16v ~/hbase-mr-tools/bin]$ hadoop streaming15/03/11 15:54:04 ERROR streaming.StreamJob: Missing required options: input, outputUsage: $HADOOP_HOME/bin/hadoop jar \          $HADOOP_HOME/hadoop-streaming.jar [options]Options:  -input    <path>     DFS input file(s) for the Map step  -output   <path>     DFS output directory for the Reduce step  -mapper   <cmd|JavaClassName>      The streaming command to run  -combiner <cmd|JavaClassName> The streaming command to run  -reducer  <cmd|JavaClassName>      The streaming command to run  -file     <file>     File/dir to be shipped in the Job jar file  -inputformat TextInputFormat(default)|SequenceFileAsTextInputFormat|JavaClassName Optional.  -outputformat TextOutputFormat(default)|JavaClassName  Optional.  -partitioner JavaClassName  Optional.  -numReduceTasks <num>  Optional.  -inputreader <spec>  Optional.  -cmdenv   <n>=<v>    Optional. Pass env.var to streaming commands  -mapdebug <path>  Optional. To run this script when a map task fails   -reducedebug <path>  Optional. To run this script when a reduce task fails   -verboseGeneric options supported are-conf <configuration file>     specify an application configuration file-D <property=value>            use value for given property-fs <local|namenode:port>      specify a namenode-jt <local|jobtracker:port>    specify a job tracker for corona-jtold <local|jobtracker:port>    specify a job tracker for common mapreduce-files <comma separated list of files>    specify comma separated files to be copied to the map reduce cluster-libjars <comma separated list of jars>    specify comma separated jar files to include in the classpath.-archives <comma separated list of archives>    specify comma separated archives to be unarchived on the compute machines.The general command line syntax isbin/hadoop command [genericOptions] [commandOptions] 

可以看到 "-D" 属于Generic options,而且"bin/hadoop command [genericOptions] [commandOptions]" 表明genericOptions 在 commandOptions之前,例如 还有一个参数设置 "-input", "-input" 不在"Generic options" 列表中,故属于 commandOptions, 所以 "-D" 一定在 "-input" 之前。

input输入相关的参数及功能

如何指定输入路径?

Java方式,程序通过命令行获取输入路径:

 FileInputFormat.setInputPaths(job, new Path(args[0]));

Streaming方式,提交job时指定-input,如:

 -input /home/mr/wuyunyun/input-dir

Tips:
1)输入路径可以为一个或多个目录/文件

用户可以使用“,”(逗号)分割多个目录/文件,如: -input /home/mr/wuyunyun/input-dir1,/home/mr/wuyunyun/input-dir2

2)输入路径支持通配

如:-input /home/mr/wuyunyun/input-dir/part-0000[0-9] //匹配目录input-dir下的文件part-00000~part-00009
-input /home/mr/wuyunyun/input-dir/*-test-* //匹配目录input-dir下包含“-test-”的所有文件做输入*

3)使用har归档文件做输入时,需指定input为har://

注:使用har归档文件支持通配符
正确用法:-input har:///home/xxxx/archive/data/20131314*.har/*/

错误用法:-input har:///home/xxxx/archive/data/2013*/*/
 [请注意!] 若har文件下有多级目录,如har:///../**.har/A/B/C.gz,input的匹配行请设置到匹配到文件的所在目录,如har:///../**.har/*/*/ [请注意!] 若需要跨集群读取har文件,如har://hdfs-namenode.safe.lycc.qihoo:9000/../*.har/*/*,请设置fs.default.name=hdfs://namenode.safe.lycc.qihoo:9000。并将提交中使用到的路径相关配置,设置为包含对应集群名的全路径。

4)如何忽略压缩损坏文件?

需要MapReduce处理过程中忽略压缩损坏的文件,需要设置参数mapred.ignore.badcompress为true

5)输入路径中带有多级目录

设置参数mapred.input.dir.recursive为true,可以支持在输入路径中递归匹配各级目录中的文件。
如:输入路径为:/home/mr/wuyunyun/input-dir1/sub-dir1/file1,/home/mr/wuyunyun/input-dir1/sub-dir2/file2,则可设置此参数为true,同时指定-input /home/mr/wuyunyun/input-dir1/ 即可匹配input-dir1中子目录中的文件

InputFormat

TextInputFormat(默认的inputFormat)
MapReduce默认的inputFormat是TextInputFormat,用于处理文本格式的输入。它的key为一行的偏移量,value为此行的内容。

如果某行的value过大,会导致OOM,建议设置一行处理最大字节数来避免job失败:mapred.linerecordreader.maxlength(单位:字节),超过设定值部分将被丢弃

CombineTextInputFormat(输入是小文件时使用)
当处理大量的小数据文件时,如果文件不能被inputformat切分(如gz压缩文件),则会启动很多的map,比如几十万个map,这样会带来几个问题:
  • 对jobtracker内存造成压力。当一个job完成初始化后,jobtracker会在内存中维护该job的所有map/reduce task的相关信息。含有大量task的job会占用jobtracker的大量内存。
  • 每个map处理的数据很小,执行时间很短,比如几秒,则task调度和启动的时间开销将会非常可观,大量的时间花费在task启动时间上。
因此需要使用CombineTextInputFormat对输入数据进行合并。

使用方法:

Streaming程序提交任务时指定:    -inputformat org.apache.hadoop.mapred.lib.CombineTextInputFormatJava new API程序    import org.apache.hadoop.mapreduce.lib.input.CombineTextInputFormat;    job.setInputFormatClass(CombineTextInputFormat.class);     //设置inputformat类Java old API程序    import org.apache.hadoop.mapred.lib.CombineTextInputFormat;    conf.setInputFormat(CombineTextInputFormat.class);    //设置inputformat类注:默认的合并大小是256M,即每个map处理256M数据,用户亦可根据数据量进行自定义设置,通过参数mapred.max.split.size(单位:字节)
如java程序设置
conf.setLong(“mapred.max.split.size”, 1073741824);//设置每个map处理的数据为1G。
Streaming程序设置:-jobconf mapred.max.split.size=1073741824


一般情况无需设置mapred.max.split.size
mapred.max.split.size设置多少合适?mapred.max.split.size用于指定每个map最多处理的数据量。单位为Bytes。一般建议每个map输出的数据量在300M以内,这样中间结果数据能保存在内存中,减少磁盘IO。可以根据map的输入/输出比决定每个mapred.max.split.size的设置。
如何严格分割为同样大小的split?根据combineInputFormat的split切分规则,要完全按照要求分割,一共要设置四个值:mapred.max.split.size = mapred.min.split.size.per.node = mapred.min.split.size.per.rack = {splitSize}mapred.max.num.blocks.per.split = 100(默认,可尽量大)
使用CombineTextInputFormat后,怎么处理不同类型的文件?
采用CombineTextInputFormat后,一个map可能处理多个文件的不同块。

对于java,仍然采用MultipleInputs对不同类型的文件采用不同的mapper进行处理(参见MultiInputs)
对于streaming,首先需要对输入文件进行分组,确保每个map处理的文件都是同一种类型的,然后再根据环境变量map_input_file环境变量判断该map处理的是何种类型文件,再做差异化处理。 使用方法如下:

hadoop streaming \-inputformat org.apache.hadoop.mapred.lib.CombineTextInputFormat \-jobconf mapred.max.split.size=1073741824 \-mapper "sh map.sh" \-reducer NONE \-input /home/xitong/data/wangzhiqiang/ \-output /home/xitong/tmp/wangzhiqiang/out1 \-file map.sh \-jobconf combineinput.pool.filters="*example0*,*example1*,*example2*" \     #注:请注意文件名中不包含“example11*,example22*”,否则匹配会异常-jobconf mapred.job.name="wangzhiqiang-test-combine-filter"

combineinput.pool.filters:指定输入文件的分组配置,注意,需要为正则式表达式,否则匹配不到该文件将不进行处理。
该例中有三类文件,分别为文件名中包含example0, example1, example2的文件。
在map中就可以通过map_input_file环境变量判断该map处理的是哪种类型的文件。 注意map_input_file对应的值是该map处理的第一个文件的HDFS存储全路径名(例如:hdfs://w-namenode.safe.zzbc.qihoo.net:9000/home/mr/data/test_multiinputs/data1)在map执行过程中该变量不会改变,也就是说,对于该map中后续处理的文件的文件名并没有反映到map_input_file环境变量里。

MapReduce怎么处理不同类型的输入文件?(MultipleInputs)

在mapreduce程序可能同时处理不同类型的文件,一种方法是使用MultipleInputs对不同输入类型文件使用不同的Mapper类分别进行处理(java),另一种方法是根据处理文件名进行不同的处理(streaming)。
  • Java方式(使用MultipleInputs示例)
 //对于/home/mr/data/test_multiinputs/data1目录,采用Map1进行处理MultipleInputs.addInputPath(conf,new Path("/home/mr/data/test_multiinputs/data1"),TextInputFormat.class,Map1.class); //对于/home/mr/data/test_multiinputs/data2目录,采用Map2进行处理MultipleInputs.addInputPath(conf,new Path("/home/mr/data/test_multiinputs/data2"),TextInputFormat.class,Map2.class);
  • Streaming方式
Streaming方式只能根据处理的文件名进行判断,文件名可以从环境变量里获取map_input_file。streaming方式如果采用CombineTextInputFormat,则可以借助对输入路径进行分组来实现(参见上节CombineTextInputFormat)

output输出相关的参数及功能

如何对输出进行压缩?

当前默认对MapReduce的输出不进行压缩,如果数据量比较大,建议采用输出压缩以节省HDFS存储空间。
用户可采用在java代码中设置压缩参数,或者在job提交时指定压缩参数

  • java代码中设置:
   conf.setBoolean("mapred.output.compress", true);   conf.set("mapred.output.compression.codec", "org.apache.hadoop.io.compress.GzipCodec");
  • 提交job时指定如下参数:
   -D mapred.output.compress=true \   -D mapred.output.compression.codec=<codec> \

常用的压缩算法指定参数:

gz压缩:-D mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodeclzo压缩:-D mapred.output.compression.codec=com.hadoop.compression.lzo.LzoCodecBzip2压缩:-D mapred.output.compression.codec=org.apache.hadoop.io.compress.BZip2Codec

OutputFormat

TextOutputFormat

输出时如何忽略多余的分隔符
对于streaming Job,当只需要输出key时,默认的TextOutputFormat会输出多余的分割符(默认为’\t’),此时可以通过设置如下参数不输出多余的分隔符。
JAVA程序:

 conf.setBoolean("mapred.textoutputformat.ignore.separator",true);

Streaming程序:

 -jobconf mapred.textoutputformat.ignore.separator =true
如何将MapReduce的一个输出写入多个文件?(TextMultiOutputFormat)

如果一个reduce输出过大(或者无reduce的map输出过大),会对下游的job产生较大的处理压力。为了避免此情况,可以采用TextMultiOutputFormat对reduce的输出进行切分,让一个reduce的输出可以至多个文件,
切分后的文件命名为:part-r/m-p-00x(其中p为partition号,part-r-00000-000),不会影响reduce的分区。

使用方式如下:

1.此功能要求输出需要必须采用输出压缩,请先确认输出压缩功能设置为true,再指定压缩方式,下面采用了gz压缩:-D mapred.output.compress=true \-D mapred.output.compression.codec=org.apache.hadoop.io.compress.GzipCodec 
2.指定outputformat为TextMultiOutputFormat:streaming程序请使用老API: -outputformat org.apache.hadoop.mapred.TextMultiOutputFormatJAVA程序的新API: 可在程序中设置:job.setOutputFormatClass(TextMultiOutputFormat.class);
注:- 当前默认的输出切分大小是512M(压缩前),可以通过参数:mapred.reduce.max.size.per.file设置切分大小,单位是字节- YARN 中此类包路径修改,使用class请用:org.apache.hadoop.mapred.lib.TextMultiOutputFormat 或者 org.apache.hadoop.mapreduce.lib.output.TextMultiOutputFormat
MapReduce如何使用多路输出?

Streaming支持多路输出(SuffixMultipleTextOutputFormat)
如下示例:

hadoop streaming \-input /home/mr/data/test_tab/ \-output /home/mr/output/tab_test/out19 \-outputformat org.apache.hadoop.mapred.lib.SuffixMultipleTextOutputFormat \   # 指定outputformat为org.apache.hadoop.mapred.lib.SuffixMultipleTextOutputFormat-jobconf suffix.multiple.outputformat.filesuffix=a,c,f,abc,cde  \             # 指定输出文件名的前缀,所有需要输出的文件名必须通过该参数配置,否则job会失败-jobconf suffix.multiple.outputformat.separator="#" \                         # 设置value与文件名的分割符,默认为“#”,如果value本身含有“#”,框架会自动匹配至最后一个分隔符,用户亦可通过该参数重新设置其他的分隔符。-mapper "cat" \-reducer "sh reduce.sh" \-file reduce.sh

注:1、标记为红色的参数必须设置,参数说明请见注释
注:2、当value为空时要在key值与"suffix.multiple.outputformat.separator"之间补充一个"\t"分隔符
注:3、输出不能有空行
注:4、key和value值中不能有换行符

Map或者reduce里需要在每个记录的reduce追加“#+文件名”

#!/bin/bash 

while read linedo key=$(echo $line | awk -F' ' '{print $1}') value=$(echo $line | awk -F' ' '{print $2}') if [ "$key" == "a" ] then echo "$key$value#a" fi

if [ "$key" == "c" ] then echo "$key$value#c" fi

if [ "$key" == "f" ] then echo "$key$value#f" fi

if [ "$key" == "abc" ] then echo "$key$value#abc" fi

if [ "$key" == "cde" ] then echo "$key$value#cde" fidone

Java如何实现多路输出?(MultiOutputs/MultiOutputFormat)

  • 下面用MultiOutputFormat实现了一个小需求:把原始的日志文件用hadoop做清洗后,按业务线输出到不同的目录下去。

核心即实现继承自MultipleOutputFormat的输出文件分类的方法,核心代码如下:

// MultipleTextOutputFormat 继承自MultipleOutputFormat,实现输出文件的分类  public static class PartitionByCountryMTOF extends          MultipleTextOutputFormat<NullWritable, Text> { // key is                                                         // NullWritable,                                                         // value is Text    protected String generateFileNameForKeyValue(NullWritable key,               Text value, String filename) {      String[] arr = value.toString().split(",", -1);      String country = arr[4].substring(1, 3); // 获取country的名称      return country + "/" + filename;    }

完整代码及示例请移步 #MultiOutputFormat实例 

  • 新API如何使用MultiOutputs?

1.在main函数中指定多输出路径的名字,指定OutputFormat类及输出Key/Value类型。

MultipleOutputs.addNamedOutput(job,"MOSInt",TextOutputFormat.class,Text.class,IntWritable.class);MultipleOutputs.addNamedOutput(job,"MOSText",TextOutputFormat.class,Text.class,Text.class);

2.在Mapper中处理:

@Overrideprotected void setup(Context context) throws IOException,InterruptedException {      mos = new MultipleOutputs<Text,IntWritable>(context);}
@Overridepublic void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException{ String line = value.toString(); String[] tokens = line.split("-"); mos.write("MOSInt",new Text(tokens[0]), new IntWritable(Integer.parseInt(tokens[1]))); // 直接输出至:/home/mr/wuyunyun/output/MOSInt-m-00000 mos.write("MOSText", new Text(tokens[0]),tokens[2]); // 直接输出至:/home/mr/wuyunyun/output/MOSText-m-00000 mos.write("MOSText", new Text(tokens[0]),line,tokens[0]+"/part"); // 输出至指定的目录下:/home/mr/wuyunyun/output/xxx/part-m-00000}
@Overrideprotected void cleanup(Context context) throws IOException,InterruptedException { mos.close();}

完整代码及示例请移步 #MultiOutputs实例

Map/Reduce执行相关参数功能

Mapper

如何设置map的输出key/value类型?

用户可以按需设置map的输出key/value类型。
Streaming方式:

默认的map输出key/value类型为io.apache.hadoop.Text,可通过如下参数设置:
-jobconf mapred.mapoutput.key.class=<类全名> -jobconf mapred.output.value.class=<类全名>如:-jobconf mapred.mapoutput.key.class=org.apache.hadoop.io.Text-jobconf mapred.output.value.class=org.apache.hadoop.io.Text

Java方式:

Java中需要显式在Mapper声明中指定map输出key/value的类型:
 public static class MyMap extends Mapper<Text, Text, Text, Text> 同时设置map输出key/value类型为声明的类型: job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(Text.class);
如何在map或reduce中获得唯一id号?

可以通过在老API中的configure()或者新API中的setup()中通过如下接口获得: 以setup()为例:

public void setup(Context context) throws IOException, InterruptedException {  //获得attempt id,如:attempt_201207171155_0009_m_000001_0  context.getConfiguration().get("mapred.task.id");      //获得task id, 如:task_201207171155_0009_m_000001      context.getConfiguration().get("mapred.tip.id");  //获得该map或reduce的索引号,即是第几个map/reduce task,去task id后面的整数  context.getConfiguration().getInt("mapred.task.partition");  //该task是map还是reduce  context.getConfiguration().getBoolean("mapred.task.is.map");  //获得job id,如:job_201207171155_0009  context.getConfiguration().get("mapred.job.id");}

Streming程序中可以通过环境变量获取,比如bash脚本

#!/bin/bashecho ${mapred_task_id} >&2  #bash中不允许变量中存在.,所以用_代替echo ${mapred_tip_id} >&2   

另外,通过-D或者-jobconf指定的自定义的参数也可以通过环境变量取得。
Streaming常用的环境变量:

环境变量名环境变量说明map_input_file当前map task处理的第一个文件路径,如:hdfs://w-namenode.safe.zzbc.qihoo.net:9000/home/mr/data/test_multiinputs/data1mapred_input_dir用户指定的输入路径,如:hdfs://w-namenode.safe.zzbc.qihoo.net:9000/home/mr/data/test_multiinputs/data1mapred_task_id当前task的attempt id,如:attempt_201207171155_0009_m_000001_0mapred_task_is_map当前运行的是map task还是reduce task,如:map task获取此变量则为true

Partitioner

如何使用自定义的partitioner?

Streaming任务默认的计算partition的方式为key.hashCode()%numReduces。
用户也可按需自己实现指定partitioner。当前MapReduce框架除了默认的hashCode

  • 写程序实现自定义的Partitioner
用户可以采用自定义的Partitioner,以决定key被送到哪个reduce中。
用户编写自定义的Partitioner类,需实现org.apache.hadoop.mapred.Partitioner接口,并实现configure(JobConf conf)和getPartition(K,V,numReduces)。

例如:

package com.qihoo.xitong.mapred;
import org.apache.hadoop.mapred.*;import org.apache.hadoop.io.Text;public class NumberialPartition implements Partitioner<Text,Text>{ public void configure(JobConf conf){} public int getPartition(Text key,Text value,int numReduces){ return Integer.parseInt(key.toString())%numReduces; }}

提交时需要将该段代码编译打包并通过-libjars指定,同时设置-partitioner参数。例如:

~/software/hadoop/bin/hadoop ~/software/hadoop/contribe/streaming/hadoop-xxxx-streaming.jar \-libjars MyPartitioner.jar \-partitioner com.qihoo.xitong.mapred.NumberialPartition
  • 按key中的某些字段计算partition(KeyFieldBasedPartitioner)(常用来做二次排序)
若需要按key中的某些字段的hash计算partiton,则可以采用KeyFieldBasedPartitioner,并设置相关的参数即可实现。

Map/Reduce框架提供了KeyFieldBasedPartitioner类,便于用户设置按key中的某些字段计算partition。

例如: 对如下输入,期望将同一个公司的记录由同一个reduce进行处理,即按照第二个字段计算partiton即可:com.qihoo.safecom.qihoo.desktopcom.baidu.zhidaocom.baidu.baike

提交命令如下:

~/software/hadoop/bin/hadoop jar ~/software/hadoop/contrib/streaming/hadoop-0.20.1.16-streaming.jar \-Dmap.output.key.field.separator="." \-Dmapred.text.key.partitioner.options=-k2,2 \-input /test/keyfield_par*.txt \-output /test/wzq/out$(date +%s) \-mapper "cat" \-reducer "cat" \-partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner \-jobconf mapred.reduce.tasks=10 \-jobconf mapred.job.name="wuyunyun_KeyFieldBasedPartition_test"参数说明:map.output.key.field.separator指定map输出的key中各字段的分隔符。注意:该参数不支持转义。比如指定’\t’分割必须做如下设置:-jobconf map.output.key.field.separator="       "mapred.text.key.partitioner.options指定按第几个字段计算partition。可以指定多个字段。例如-k2,3表示按第2-3个字段计算partition

Comparator

默认的Comparator
KeyFieldBasedComparator(非字节排序,如按照数字排序)

Streaming默认情况下对key按照字典序进行排序。如果需要按照key的某个字段进行排序需要采用KeyFieldBasedComparator,并设置相关的参数。
例如:

对于如下的map输出,期望按照key(前两列)中第一列按字典序,第二列按数字进行排序。com.sina100value_sina1com.sina101value_sina2com.sina99value_sina3com.baidu28value_baidu1com.baidu26value_baidu2com.baidu122value_baidu3
若只设置一个reduce,则预期reduce处理的顺序如下:com.baidu26value_baidu2com.baidu28value_baidu1com.baidu122value_baidu3com.sina99value_sina3com.sina100value_sina1com.sina101value_sina2
提交命令如下:/home/work/software/hadoop/bin/hadoop jar /home/software/hadoop/contrib/streaming/hadoop-xxx-streaming.jar \ -Dmapred.output.key.comparator.class=org.apache.hadoop.mapred.lib.KeyFieldBasedComparator \ -Dstream.map.output.field.separator="" \ -Dstream.num.map.output.key.fields=2 \ -Dmapred.text.key.comparator.options="-k1,1 -k2,2n" \ -Dmapred.job.name="wuyunyun_keyFieldBasedComparator_test" \ -mapper "cat" \ -reducer "cat" \ -input /test/keyfield_numsort.txt \ -output /test/wzq/out$(date +%s)

参数说明:

mapred.output.key.comparator.class 参数指定对key进行比较所用的comparator类,此处指定为org.apache.hadoop.mapred.lib.KeyFieldBasedComparator,用户可以自己实现comparator,通过该参数指定,使用自定义的comparator。stream.num.map.output.key.fields 参数指定用哪几个列作为key。mapred.text.key.comparator.options 参数指定用于排序的列和排序方法。例如:“-k2,2n”指定按第2列以numerical方式由小到大排序,“-k2,2nr” 指定按第2列以numerical方式由大到小排序。
排序加速

Map输出排序系统默认在一个大buffer内先对partition进行排序,再对parition内的key进行排序。经过优化的map排序是将一个大buffer变为多个小buffer,每个小buffer即对应一个partition,排序时各个小buffer进行内部排序。对于瓶颈在MapReduce Write的job,此优化可以提升10%左右的性能,用户只需要在job提交时设置:mapred.map.output.blockcollector=true, 即可生效。

但不是所有情况都适合使用此优化功能:
  1. 对于瓶颈在于“读”或者用户处理逻辑中时,map排序优化的效果可以忽略不计;
  2. 此优化,暂时只支持key/value类型同时为“org.apache.hadoop.Text/BytesWritable”类型,map的K/V类型不符合的系统会自动采用默认排序方式;
  3. 由于此优化采用字节序排序方式,所以不支持用户指定的keyCompartor;
  4. 此优化不适合reduce过多(>=10000个)且reduce数据倾斜的job,会因为map输出的index文件过多导致OOM。

Combiner

Map端如何使用Combiner?

Mapreduce中的Combiner就是为了避免map任务和reduce任务之间的数据传输而设置的,Hadoop允许用户针对map task的输出指定一个合并函数,即减少传输到Reduce中的数据量。它主要是为了削减Mapper的输出从而减少网络带宽和Reducer之上的负载。
Combiner可以看做是Map阶段的reduce,系统中没有默认的combiner,需要用户自己实现,并显示设置。
例如:

 Streaming方式,提交job时指定: -combiner <Combiner可执行脚本 OR Combiner的Java类名>
Java方式,程序中设置: conf.setCombinerClass(Combiner.class);

如果Reducer只运行简单的分布式方法,例如最大值、最小值、或者计数,那么我们可以让Reducer自己作为Combiner。
注:combine的输入和reduce的完全一致,输出和map的完全一致

常用的Combiner示例

Reducer

如何将Counters写入_SUCCESS文件?

将counters写入_SUCCESS文件,设置参数mapred.success.file.status为true

Streaming方式,提交Job时指定:-D/-jobconf mapred.success.file.status=true
Java方式,在程序中设置:conf.setBoolean();Java方式同样可以向Streaming方式在提交job时指定参数,请参见5.1.3节job提交时指定参数不生效
如何设置MapReduce的语言环境?

为了避免mapreduce集群中语言环境不一致可能造成的结果异常,建议在提交任务是指定语言环境。以设置en_US.UTF-8为例:
JAVA程序:

conf.set("mapred.child.env","LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8");

Streaming程序:

-jobconf mapred.child.env="LANG=en_US.UTF-8,LC_ALL=en_US.UTF-8"

MR TIPS

如何自定义计数器和进行状态汇报?

Java方式:
用户可自定义counter的GroupName和CounterName,并通过Context获取counter进行操作,例如:

public class Reducer<Text, IntWritable, Text, IntWritable> {  public String groupName="My_Counters";  public String counterName="KEY_COUNT";  void reduce(Textkey, Iterable<IntWritable> value, Context context) throws IOException, InterruptedException {      int sum = 0;      for(IntWritable value: values) {        sum += val.get();      }      context.write(key, new IntWritable(sum));      context.getCounter("My_Counters","KEY_COUNT").increment(1);   // 示例自定义counter统计KEY的个数    }}

Streaming方式:
Streaming程序通过向标准错误流写入特殊格式的字符串进行计数和状态汇报。
计数的格式如下:

reporter:counter:<group>,<counter>,<amount> 

状态汇报的格式如下:

reporter:status:<message>

如下是一个shell程序进行计数和状态汇报的例子:

#!/bin/bashwhile read linedo    echo "$line"    echo "reporter:counter:MapCounter,ReadCounter,1" >&2    echo "reporter:status:read one line" >&2done
Streaming程序当map或reduce返回值非0时,整个任务会失败?

对于Streaming程序,MR框架会判断map和reduce的返回值,如果返回值非0,则认为map或reduce执行中出现错误,重试数次(默认为4)后仍然失败,会导致整个任务失败。
如果返回值非0属于正常情况,则有两种方法规避任务失败:

  1. 可以通过设置参数:stream.non.zero.exit.is.failure=false,即使map/reduce失败,整个任务也不会失败。
  2. 修改或封装参数,使可预期正常结束的情况的返回值设置为0。
 请注意,streaming程序中若直接使用命令,可能会因为命令失败,导致返回值非0,从而导致Job失败。 请尽量封装命令到脚本中执行,并在脚本中添加exit 0,保证返回值为0  [EXAMPLE] 常见失败命令: grep ***
Streaming任务如何指定key,value的分隔符?

Streaming任务默认以‘\t’对输入和输出的key/value进行分割。即:第一个’\t’之前的字符串作为key,余下的字符串作为value。若整行都没有’\t’分隔符,则整行作为key,value为空。
可以通过设置参数stream.map.output.field.separator和参数stream.num.map.output.key.fields自定义分隔符和key。 例如设置:

-D stream.map.output.field.separator="_" \-D stream.num.map.output.key.fields=3 \
则对于”aa_bb_cc_dd”,key为”aa_bb_cc”,value为”dd”
Streaming程序如何判断管道的每个进程返回值?

rets="${PIPESTATUS[*]}"

ret=$?if [ $ret -ne 0 ]then  exit $retfi
for ret in $retsdo if [ "$ret" != "0" ] thenecho "PIPESTATUS $rets" 1>&2 exit $ret fidone

Job调度参数功能

如何设置JobName?

我们期望用户可以主动指定有意义JobName,这样不仅方便我们沟通联系,而且谨防job失败后推送邮件轰炸其他同事。 请指定如下参数:

 mapred.job.name="xxx_test"  其中xxx为您的邮箱域账户

如何设置Reduce的个数?

如果用户不主动设置Reduce个数,系统默认启动一个Reduce。如果用户处理数据量过大(>10G),则会影响Reduce的处理速度。 请您根据map的输出量合理设置reduce的个数,请确保每个reduce处理的数据不超过10G 通过如下参数指定reduce个数,单位(个):

mapred.reduce.tasks

如何限制Job的task并发数?

通过如下参数控制,单位(个):

mapred.job.max.map.runningmapred.job.max.reduce.running

如果Job已经提交,可以在运行时通过hadoop命令,修改map/reduce task的并发数:

hadoop job -set-max-task-running <job-id> <task-type> <max-task-running>

例如: hadoop job -set-max-task-running job_201404080925_130068 map 500

如何设置Job的失败比例?

默认情况下,一个task重试4次失败,就会导致这个job失败。 如果用户对job的结果不要求严格无误,且不想个别task的失败而导致整个job的失败,可设置map/reduce的容错率:

mapred.max.map.failures.percentmapred.max.reduce.failures.percent参数单位为整型,例如设置mapred.max.map.failures.percent=3 表示此job允许3%的task失败

如何设置Job的优先级?

Job的优先级设置值有:LOW、VERY_LOW、NORMAL、HIGH、VERY_HIGH 五种类型,默认情况下,一般普通的job优先级默认是NORMAL。系统会根据map的数据个数自动设置优先级,map个数较小的job,优先级会较高。 通过如下参数,用户可以根据自己业务的紧急性,设置Job的优先级:

mapred.job.priority

如何设置task的超时时间?

默认情况下,如果一个task 10min没有进行MapReduce的读/写,就会失败。 为满足特殊业务需求,例如处理逻辑本身时间>10min的,可以通过如下参数调整task的超时时间,请您根据处理输出量合理预估时间:

mapred.task.timeout(单位:毫秒)

注意:非特殊长逻辑处理,我们不建议您修改此参数的默认值。否则会带来未知的Job问题,比如job hang住不结束。

如何打开/关闭预测执行?

预测执行是MapReduce框架中一个重要的优化,其主要是为了解决慢节点导致的task处理慢问题。 它的原理是:如果一个task运行比较慢,系统会在另一个计算节点启动一个与此task完全相同的task,哪个task先完成,则kill掉另一个,并将完成的task的结果移至最终的输出目录中。这也就是为什么您会在正常的job执行页面看到一些kill的task。 默认情况下,系统的预测执行功能是打开状态,对于写数据库或者hbase这样的存储,需要关闭预测执行,不能同时写。 控制预测执行的参数:

mapred.map.tasks.speculative.execution  // 默认打开为true,关闭请显式设置为falsemapred.reduce.tasks.speculative.execution  // 默认打开为true,关闭请显式设置为false

如何设置slow start?

Slow start也是MapReduce框架的一个优化,它代表reduce在何时开始调度启动。当前我们的集群配置为85%,即map task完成85%的时候,开始启动reduce,让其进行shuffle操作,从已完成的map拖数据。这样的好处是尽量使reduce和map可以并行起来。 
但是如果map有个别长尾(完成率>85%),则reduce会全部起来,并且一直处于shuffle阶段,等待map的完成,这样会造成reduce的槽位被无辜占用,其他的job会因为得不到reduce槽位资源而被堵住。 
所以您可以通过如下参数,设置适合job运行的slow start比例(不得低于85%)

 mapred.reduce.slowstart.completed.maps   
比如设置mapred.reduce.slowstart.completed.maps=1,则表明关闭slow start,即等所有map运行完成后在启动reduce; mapred.reduce.slowstart.completed.maps=0.9,则表明map运行完成90%的时候再启动reduce。

MapReducejob常见问题及解决方法

如何找到失败task的对应输入?

在task的all task页面,从task log输出中可以查到。通过定位syslog logs,查询关键字“org.apache.hadoop.mapred.MapTask: split:”,即为task的输入文件。

当task失败时,可以定位到导致失败的单个task,将其split作为输入,在集群进行测试,以检查是否数据问题。

TIP:task的输入partId与taskId并不匹配

如何判断job的运行结果?

有两种方法:1、通过脚本提交的job,通过$?返回值判断是否非0,为0则为成功,非0为失败。2、通过check输出目录,是否存在_SUCCESS标记,以判断是否成功运行完成。

TIP:第一种方法可能存在提交进程被杀死的情况,这时的返回值可能也非0,但job可能已被正常提交。

如何处理syslog输出超限的错误?

使用自定义的log4j文件,将输出较多的class设置为ERROR LEVEL。

1、log4j设置示例:常见客户端log4j文件位置:/usr/bin/hadoop/software/hadoop/conf/log4j.properties调整两个package log输出为ERROR级别:log4j.logger.org.apache.hadoop.hbase=ERRORlog4j.logger.org.apache.hadoop.hdfs=ERROR注:在所需设置类前加log4j.logger.


2、作业提交示例:~/software/hadoop/bin/hadoop streaming-Dmapreduce.user.classpath.first=true-input /home/work/bashrc -output /home/work/zhongliang-xy/log4j-test2 -mapper "cat" -reducer NONE-file log4j.properties

  注:java方式请用-libjars 上传log4j.properties

其他

请移步 MapReduce失败Job常见问题及解决方法

如何自主部署hadoop不支持的运行环境

php的mcrypt扩展示例

通过命令行将依赖的扩展文件上传至hdfs:

-file ./mcrypt.so-file ./libmcrypt.so.4

设置依赖的lib环境:

export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.;

设置php.ini中变量值:

php -d extension_dir=. -d enable_dl=on

mapper中load扩展包:

dl('mcrypt.so')

附录

MapReduce常见参数表

参数名参数说明mapred.job.name指定jobNamemapred.reduce.tasks指定reduce个数mapred.job.priority指定job的优先级:LOW、VERY_LOW、NORMAL、HIGH、VERY_HIGHmapred.input.dir使用-input指定job的输入路径mapred.output.dir使用-output指定job的输出路径mapred.output.compress默认为false,如需要对输出进行压缩请设置为truemapred.output.compression.codec若采用压缩,此参数用来指定压缩方式,常用的压缩方式:org.apache.hadoop.io.compress.GzipCodec;org.apache.hadoop.io.compress.DefaultCodec等mapreduce.user.classpath.first优先使用用户jar包请设置为true,默认为falsemapred.job.max.map.running限制job的map task并发数mapred.job.max.reduce.running限制job的reduce task并发数mapred.use.multimembergzip支持一个gz文件有多个压缩块组成,集群上已经默认打开。用户无需设置mapred.max.map.failures.percent/mapred.max.reduce.failures.percent如果对job的结果不要求严格无误,且不想个别task的失败而导致整个job的失败,可设置map/reduce的容错率,参数为int型,如5表示允许5%的task失败

MultiOutputFormat实例

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.conf.Configured;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.LongWritable;import org.apache.hadoop.io.NullWritable;import org.apache.hadoop.io.Text;import org.apache.hadoop.mapred.FileInputFormat;import org.apache.hadoop.mapred.FileOutputFormat;import org.apache.hadoop.mapred.JobClient;import org.apache.hadoop.mapred.JobConf;import org.apache.hadoop.mapred.MapReduceBase;import org.apache.hadoop.mapred.Mapper;import org.apache.hadoop.mapred.OutputCollector;import org.apache.hadoop.mapred.Reporter;import org.apache.hadoop.mapred.TextInputFormat;import org.apache.hadoop.mapred.lib.MultipleTextOutputFormat;import org.apache.hadoop.util.Tool;import org.apache.hadoop.util.ToolRunner;
public class MultiFile extends Configured implements Tool { public static class MapClass extends MapReduceBase implements Mapper<LongWritable, Text, NullWritable, Text> { @Override public void map(LongWritable key, Text value, OutputCollector<NullWritable, Text> output, Reporter reporter) throws IOException { output.collect(NullWritable.get(), value); } }
// MultipleTextOutputFormat 继承自MultipleOutputFormat,实现输出文件的分类 public static class PartitionByCountryMTOF extends MultipleTextOutputFormat<NullWritable, Text> { // key is // NullWritable, // value is Text protected String generateFileNameForKeyValue(NullWritable key, Text value, String filename) { String[] arr = value.toString().split(",", -1); String country = arr[4].substring(1, 3); // 获取country的名称 return country + "/" + filename; } }
// 此处不使用reducer /* * public static class Reducer extends MapReduceBase implements * org.apache.hadoop.mapred.Reducer<LongWritable, Text, NullWritable, Text> * { * @Override public void reduce(LongWritable key, Iterator<Text> values, * OutputCollector<NullWritable, Text> output, Reporter reporter) throws * IOException { // TODO Auto-generated method stub * } * * } */
@Override public int run(String[] args) throws Exception { Configuration conf = getConf(); JobConf job = new JobConf(conf, MultiFile.class);
Path in = new Path(args[0]); Path out = new Path(args[1]);
FileInputFormat.setInputPaths(job, in); FileOutputFormat.setOutputPath(job, out); job.setJobName("wuyunyun_MultiFile"); job.setMapperClass(MapClass.class); job.setInputFormat(TextInputFormat.class); job.setOutputFormat(PartitionByCountryMTOF.class); job.setOutputKeyClass(NullWritable.class); job.setOutputValueClass(Text.class); job.setNumReduceTasks(0); JobClient.runJob(job); return 0; }
public static void main(String[] args) throws Exception { int ret = ToolRunner.run(new Configuration(), new MultiFile(), args); System.exit(ret); }}

测试数据:

$~/software/hadoop/bin/hadoop fs -cat /home/mr/wuyunyun/test-input/multiTest5765303,1998,14046,1996,"AD","",,1,12,42,5,59,11,1,0.4545,0,0,1,67.3636,,,,5785566,1998,14088,1996,"AD","",,1,9,441,6,69,3,0,1,,0.6667,,4.3333,,,,5894770,1999,14354,1997,"AD","",,1,,82,5,51,4,0,1,,0.625,,7.5,,,,5765303,1998,14046,1996,"CN","",,1,12,42,5,59,11,1,0.4545,0,0,1,67.3636,,,,5785566,1998,14088,1996,"CN","",,1,9,441,6,69,3,0,1,,0.6667,,4.3333,,,,5894770,1999,14354,1997,"CN","",,1,,82,5,51,4,0,1,,0.625,,7.5,,,,

测试结果:

$ ~/software/hadoop/bin/hadoop fs -ls /home/mr/wuyunyun/out1/*/*-rw-r--r--   2 mr mr        214 2014-04-14 15:56 /home/mr/wuyunyun/out1/AD/part-00000-rw-r--r--   2 mr mr        214 2014-04-14 15:56 /home/mr/wuyunyun/out1/CN/part-00000-rw-r--r--   2 mr mr          0 2014-04-14 15:56 /home/mr/wuyunyun/out1/_SUCCESS


MultiOutputs实例

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;import org.apache.hadoop.conf.Configured;import org.apache.hadoop.fs.Path;import org.apache.hadoop.io.Text;import org.apache.hadoop.io.IntWritable;import org.apache.hadoop.io.LongWritable;import org.apache.hadoop.mapreduce.*;import org.apache.hadoop.mapreduce.lib.output.*;import org.apache.hadoop.util.Tool;import org.apache.hadoop.util.ToolRunner;import org.apache.hadoop.mapreduce.lib.input.*;
public class TestwithMultipleOutputs extends Configured implements Tool { public static class MapClass extends Mapper<LongWritable,Text,Text,IntWritable> { private MultipleOutputs<Text,IntWritable> mos; protected void setup(Context context) throws IOException,InterruptedException { mos = new MultipleOutputs<Text,IntWritable>(context); }
public void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException{ String line = value.toString(); String[] tokens = line.split("-"); mos.write("MOSInt",new Text(tokens[0]), new IntWritable(Integer.parseInt(tokens[1]))); // 直接输出至:/home/mr/wuyunyun/output/MOSInt-m-00000 mos.write("MOSText", new Text(tokens[0]),tokens[2]); // 直接输出至:/home/mr/wuyunyun/output/MOSText-m-00000 mos.write("MOSText", new Text(tokens[0]),line,tokens[0]+"/part"); // 输出至指定的目录下:/home/mr/wuyunyun/output/xxx/part-m-00000,其中xxx为tokens[0]即key值 }
protected void cleanup(Context context) throws IOException,InterruptedException { mos.close(); } }
public int run(String[] args) throws Exception { Configuration conf = getConf(); Job job = new Job(conf); job.setJarByClass(TestwithMultipleOutputs.class); Path in = new Path(args[0]); Path out = new Path(args[1]);
FileInputFormat.setInputPaths(job, in); FileOutputFormat.setOutputPath(job, out); job.setMapperClass(MapClass.class); job.setNumReduceTasks(0); job.setJobName("wuyunyun_TestwithMultipleOutputs");
MultipleOutputs.addNamedOutput(job,"MOSInt",TextOutputFormat.class,Text.class,IntWritable.class); MultipleOutputs.addNamedOutput(job,"MOSText",TextOutputFormat.class,Text.class,Text.class);
System.exit(job.waitForCompletion(true)?0:1); return 0; }
public static void main(String[] args) throws Exception { int ret = ToolRunner.run(new Configuration(), new TestwithMultipleOutputs(), args); System.exit(ret); }}

测试数据:

abc-1232-hdfabc-123-rtdioj-234-grjthntg-653-sdgfvdkju-876-btyunbhm-530-bhythfter-45642-bhgfbgrfg-8956-fmghjnhdf-8734-adfbgfntg-68763-nfhsdfntg-98634-dehuyhfter-84567-drhuk

测试结果:

$ ~/software/hadoop/bin/hadoop fs -ls /home/mr/wuyunyun/output/*/*-rw-r--r--   2 mr mr        115 2014-04-14 21:20 /home/mr/wuyunyun/output/MOSInt-m-00000-rw-r--r--   2 mr mr        124 2014-04-14 21:20 /home/mr/wuyunyun/output/MOSText-m-00000-rw-r--r--   2 mr mr          0 2014-04-14 21:20 /home/mr/wuyunyun/output/_SUCCESS-rw-r--r--   2 mr mr         33 2014-04-14 21:20 /home/mr/wuyunyun/output/abc/part-m-00000-rw-r--r--   2 mr mr         22 2014-04-14 21:20 /home/mr/wuyunyun/output/bgrfg/part-m-00000-rw-r--r--   2 mr mr         17 2014-04-14 21:20 /home/mr/wuyunyun/output/bhm/part-m-00000-rw-r--r--   2 mr mr         47 2014-04-14 21:20 /home/mr/wuyunyun/output/hfter/part-m-00000-rw-r--r--   2 mr mr         18 2014-04-14 21:20 /home/mr/wuyunyun/output/ioj/part-m-00000-rw-r--r--   2 mr mr         24 2014-04-14 21:20 /home/mr/wuyunyun/output/jnhdf/part-m-00000-rw-r--r--   2 mr mr         18 2014-04-14 21:20 /home/mr/wuyunyun/output/kju/part-m-00000-rw-r--r--   2 mr mr         60 2014-04-14 21:20 /home/mr/wuyunyun/output/ntg/part-m-00000-rw-r--r--   2 mr mr          0 2014-04-14 21:20 /home/mr/wuyunyun/output/part-m-00000

hhvm实例

高性能php版本hhvm例子:

     hadoop streaming \     -input /home/xitong/tmp/test/data \     -output /home/xitong/tmp/test/output/out3 \     -mapper "./hhvm/bin/hhvm test.php" \     -reducer "cat" \     -file test.php \     -cacheArchive /home/mr/share/hhvm-2.3.2.tar.gz#hhvm
     为了发挥hhvm的性能优势,将代码封装成一个函数效果更好,例如test.php中把核心逻辑封装成fun()函数,然后调用fun()函数     cat test_hhvm.php     <?php     function fun() {       $n = 0;       while(!feof(STDIN)){         $line = fgets(STDIN);         $n++;       }       echo "$n\n";     }     fun();     ?>

pypy实例

高性能python版本pypy例子(由hadoop粉丝 范晓恺 <fanxiaokai@360.cn> 提供):

[PyPy][1]作为CPython的高性能替代方案,目前已经十分成熟,得益于JIT等技术的应用,多数场景下PyPy都具有更好的性能。
以通过正则对Nginx日志进行解析为例,对300G数据进行处理,PyPy对比原生Python能得到2.5倍以上的性能提升:

 Slot time(mins)Read(%)Write(%)User(%)MB/sRecord/sPython 2.62412.763.17%32.21%64.61%2.071741.74PyPy 2.3869.379.01%87.13%3.85%5.694809.06

在MapReduce任务中,使用官方支持的[Portable PyPy][2]能够比较完美地解决对运算性能以及运行环境的灵活配置的要求,该版本在CentOS 5上编译完成,对运行环境系统/链接库的要求很低,一般现在的RHEL/Centos/Ubuntu/Debian系统都能直接运行:

   % ldd pypy       linux-vdso.so.1 =>  (0x00007fff959ff000)       libdl.so.2 => /lib64/libdl.so.2 (0x000000392e200000)       libm.so.6 => /lib64/libm.so.6 (0x000000302fe00000)       libz.so.1 => /lib64/libz.so.1 (0x00000039fe600000)       librt.so.1 => /lib64/librt.so.1 (0x000000381a600000)       libbz2.so.1 => /lib64/libbz2.so.1 (0x0000003031e00000)       libcrypt.so.1 => /lib64/libcrypt.so.1 (0x000000392e600000)       libutil.so.1 => /lib64/libutil.so.1 (0x0000003032a00000)       libncurses.so.5 => /lib64/libncurses.so.5 (0x0000003270600000)       libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x0000003031200000)       libpthread.so.0 => /lib64/libpthread.so.0 (0x000000302fa00000)       libc.so.6 => /lib64/libc.so.6 (0x000000302f600000)       /lib64/ld-linux-x86-64.so.2 (0x000000302ee00000)       libfreebl3.so => /lib64/libfreebl3.so (0x000000392ee00000)       libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003030e00000)

pypy使用示例:

   hadoop streaming \       -D mapred.job.name=pypy-test       -cacheArchive /home/mr/share/pypy.tar.gz#pypy       -input /path/to/input       -output /path/to/output       -file map.py       -mapper "./pypy/bin/pypy map.py"
原创粉丝点击