代码示例:
public static class TestMapper extends Mapper{
private String isOut ="out" ;//错误处
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String line=value.toString();
String tmp[]=line.split( ";",-1);
if (tmp.length !=2){
isOut="in" ;
}
if(isOut .equals("out")){
context.write( new Text(key.toString()), new Text(context.toString()));
}
}
}
现象及后果:
所有被分到一个map里的record,一旦出现以“;”为分割符,长度不为2的,那么后面的record都不会被输出了。
原因分析:
这个错误跟mapreduce的计算机制有关,TestMapper继承了Mapper之后,整个类的实例变量只在启动的时候初始化一次,也就是说
private String
isOut="out"这句话只会在最开始执行一次。map函数里面的则是每次都执行(局部变量每次都重新算)。也就是说一旦出现一条数据走到了
inOut="in"。那么这个变量直到结束都是in,除非后面还有逻辑会去修改isOut的值。
那么如果map里面用到了实例变量并且会去修改它那么record之间就是可以相互干扰的。
建议:
对于这种实例变量,如果在map函数中要根据计算结果进行修改,而对于每一条记录又希望这个值是独立的,不受其他record计算影响的,那么首先在map函数刚开始给他一个初始值,或者clear掉原来的数值。
reduce也是同理的。
public static class TestMapper extends Mapper{
protected void map(LongWritable key, Text value, Context context)
throws IOException, InterruptedException {
String isOut ="out" ;//修改处
String line=value.toString();
String tmp[]=line.split( ";",-1);
if (tmp.length !=2){
isOut="in" ;
}
if(isOut .equals("out")){
context.write( new Text(key.toString()), new Text(context.toString()));
}
}
}
危险系数:5颗地雷。
因为这种bug的现象就是有的record计算结果是对的,有的是错的,而在进行测试验证的时候也很有可能拿到的都是计算正确的。