Hadoop开发基础知识记录(3)

(19)在大多数情况下,一个split里的数据(由一个mapper所处理)是来自于同一个文件的;少数情况下,一个split里的数据是来自多个文件的。

(20)org.apache.Hadoop.mapreduce.lib.output 和 org.apache.hadoop.mapreduce.output 这两个package都有 TextOutputFormat 类,其中,前者比后者版本新,使用的时候注意。

(21)执行Map-Reduce Java程序时,传入 -D hadoop.job.ugi=hadoop,hadoop 参数可以使得该job以hadoop用户来执行,例如,你是以Linux root用户来执行一个脚本,脚本中执行了一个M-R Java程序,那么该程序就无法将输出结果写入到HDFS上的 /user/hadoop/ 目录下,如果按上面的方法传入一个参数,就解决了这个问题:

hadoop com.codelast.DoSomething -D hadoop.job.ugi=hadoop,hadoop
其中,com.codelast.DoSomething是你的M-R Java程序。

(22)用MRUnit怎么测试含有FileSplit.getPath()的方法
如果mapper中的一个方法myMethod(Context context)含有如下代码片段:

String fileName = ((FileSplit) context.getInputSplit()).getPath().getName();
这句话是用来取当前mapper正在处理的文件名。那么,方法myMethod()就不能用MRUnit来测,因为无法使用MRUnit来设置mapper中当前正在处理的文件。为了测这个方法,你需要把上面的代码段抽取出来,单独放在一个方法中,我们假设其为:

public int getName(Context context) {
    return ((FileSplit) context.getInputSplit()).getPath().getName();
}
然后,在单元测试文件中,你的tester类里重写这个方法,自己指定一个返回值:

@Test
public void test_1() throws IOException {
 mapper = new MyMapper() {
   @Override
   public int getName(Context context) {
  return "part-r-00000";
   }
 };

Configuration configuration = new Configuration();
 mapDriver.withConfiguration(configuration);
 mapDriver.withMapper(mapper);
 
 mapDriver.withInput(new LongWritable(1), new Text("XXXXXX"));
 //TODO:
}
其中,MyMapper是你的mapper类名,在这里我们强制指定了getName方法返回一个字段串“part-r-00000”,从而在下面的“//TODO:”测试代码中,就可以在调用待测的myMethod方法时(间接地会调用getName方法),自然会得到“part-r-00000”这个字符串。

(23)HBase中的Pair类
如果你只要保存一对对象,那么Map可能不好用,你可以用 org.apache.hadoop.hbase.util 包中的 Pair<T1, T2> 类:

Pair<String, String> aPair = new Pair<String, String>("abc", "def");
String firstStr = aPair.getFirst();
String secondStr = aPair.getSecond();
显然,getFirst()方法用于取第一个值,getScond()方法用于取第二个值。

(24)用MRUnit测试mapper时,如何避开从 DistributedCache 加载文件
可以在unit test里set一个值到 Configuration 对象中,在mapper里判断这个变量是否set了,set了就从用于测试的local file读数据,没有set就从DistributedCache读文件。

(25)只有map的job,如何在一定程度上控制map的数量
如果一个job只有map,那么,map的数量就是输出文件的数量,为了能减少输出文件的数量,可以采用减少map的数量的方法,那么,如何减少呢?其中一个办法是设置最小的input split size。例如以下代码:

FileInputFormat.setMinInputSplitSize(job, 2L * 1024 * 1024 * 1024);
将使得小于 2G 的输入文件不会被分割处理。如果你的输入文件中有很多都是小于2G的,并且你的Hadoop集群配置了一个split的大小是默认的64M,那么就会导致一个1点几G的文件就会被很多个map处理,从而导致输出文件数量很多。使用上面的方法设置了min input split size之后,减小输出文件数量的效果很明显。

(26)如何使用elephant-bird的 LzoTextOutputFormat 对纯文本数据进行LZO压缩
假设你有一堆纯文本数据,要将它们用LZO来压缩,那么,可以用elephant-bird的 LzoTextOutputFormat 来实现。
一个只有map的job就可以完成这个工作,你需要做的,首先是设置输出格式:

job.setMapperClass(MyMapper.class);
job.setOutputFormatClass(LzoTextOutputFormat.class);
其次,你需要这样的一个mapper类:

public static class MyMapper extends Mapper<LongWritable, Text, NullWritable, Text> {

protected void map(LongWritable key, Text value, Context context) throws IOException, InterruptedException {
    context.write(null, value);
  }
}
其余代码此处省略。

(27)如何使MapReduce job执行开始时不检查某目录是否已经存在
如果M-R job的HDFS输出目录已经存在,那么job执行时会报错。为了让它不检查,或者改变默认的检查办法(例如,我们会在HDFS输出目录下生成几个子目录,在里面输出最终数据,只要确保这几个子目录不存在即可),那么就需要override checkOutputSpecs 这个方法:

@Override
  public void checkOutputSpecs(JobContext job) throws IOException {
    //TODO:
  }
在这里面,你只要把exception吃掉即可使得输出目录存在时不会报错。

(28)使用HBase的程序报错“java.lang.NoSuchMethodError: org.apache.hadoop.hbase.client.HTable.<init>”的一个原因
如果你的程序使用了HBase,并且有HDFS操作(即使用了hadoop的jar包),那么出现上面所说的错误提示时,请检查Hadoop的安装路径下的lib目录下,HBase的jar包版本是否与你的程序路径下的HBase jar包版本相同,如果不同,那么就有可能导致这个问题。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/6278fe9b9d031c825cafc8588c82e270.html