(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包版本相同,如果不同,那么就有可能导致这个问题。