说明
在一次Mapreduce任务处理中,碰到了一个有趣的问题,关于在reduce过程中 Iterable<Text>
获取值的问题。
问题重现
待处理的文本文件内容:
20012 小明
20015 小红
20012 王伟
20013 刘浩
20015 王明
20015 王亮
20015 曾仓
20012 刘工
目标代码:
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException{
// 需求 获取valius的Text值,保存在list中
...
}
看几个错误示例:
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException{
logger.info("====================reduce@ key:"+key.toString());
List<Text> texts = Lists.newArrayList(values);
for (Text text : texts) {
logger.info("=========================reduce text: "+text);
}
logger.info("-------------------------------------------------");
context.write(key, null);
}
打印结果是什么?反正我是第一次就弄错了。
[INFO ] 2019-04-25 20:01:57,138 mapred.skip.on is deprecated. Instead, use mapreduce.job.skiprecords
[INFO ] 2019-04-25 20:01:57,138 ====================reduce@ key:20012
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20012 小明
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20012 小明
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20012 小明
[INFO ] 2019-04-25 20:01:57,138 -------------------------------------------------
[INFO ] 2019-04-25 20:01:57,138 ====================reduce@ key:20013
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20013 刘浩
[INFO ] 2019-04-25 20:01:57,138 -------------------------------------------------
[INFO ] 2019-04-25 20:01:57,138 ====================reduce@ key:20015
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20015 小红
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20015 小红
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20015 小红
[INFO ] 2019-04-25 20:01:57,138 =========================reduce text: 20015 小红
[INFO ] 2019-04-25 20:01:57,138 -------------------------------------------------
[INFO ] 2019-04-25 20:01:57,154 Task:attempt_local1219496750_0001_r_000000_0 is done. And is in the process of committing
这个就是结果。。。。。。没错存储在list中的值都变成了存进去的最后一个值。
再看个示例:
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException{
logger.info("====================reduce@ key:"+key.toString());
// 这里换成了iterator
List<Text> texts = Lists.newArrayList(values.iterator());
for (Text text : texts) {
logger.info("=========================reduce text: "+text);
}
logger.info("-------------------------------------------------");
context.write(key, null);
}
结果和上面是一样的。。。。都是重复值
那我不用工具类了,自己写总可以了吧。
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException{
logger.info("====================reduce@ key:"+key.toString());
List<Text> texts = new ArrayList<Text>();
// 存贮text
for (Text value : values) {
texts.add(value);
}
// 输出
for (Text text : texts) {
logger.info("=========================reduce text: "+text);
}
logger.info("-------------------------------------------------");
context.write(key, null);
}
可是最终结果,都是一样的。
其实这三种代码,归根结底都是第三种代码。呢么为什么呢?为什么都是重复的最后一个放进去的值呢?
那我们再看另一种代码:
@Override
protected void reduce(Text key, Iterable<Text> values, Context context) throws IOException, InterruptedException{
logger.info("====================reduce@ key:"+key.toString());
List<Text> texts = new ArrayList<Text>();
// 存贮text
for (Text value : values) {
texts.add(new Text(value)); // texts.add(new Text(value.toString()));
}
// 输出
for (Text text : texts) {
logger.info("=========================reduce text: "+text);
}
logger.info("-------------------------------------------------");
context.write(key, null);
}
注意看texts.add(new Text(value)); // texts.add(new Text(value.toString()));
这一句。
结果如下:
[INFO ] 2019-04-25 20:14:25,189 ====================reduce@ key:20012
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20012 刘工
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20012 王伟
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20012 小明
[INFO ] 2019-04-25 20:14:25,189 -------------------------------------------------
[INFO ] 2019-04-25 20:14:25,189 ====================reduce@ key:20013
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20013 刘浩
[INFO ] 2019-04-25 20:14:25,189 -------------------------------------------------
[INFO ] 2019-04-25 20:14:25,189 ====================reduce@ key:20015
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20015 曾仓
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20015 王亮
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20015 王明
[INFO ] 2019-04-25 20:14:25,189 =========================reduce text: 20015 小红
[INFO ] 2019-04-25 20:14:25,189 -------------------------------------------------
和上面完全不一样,这才是正确的结果!!!!
所以在reduce过程中一定要实例化Text
PS:下面的都是我的猜测,当不了真。
当你遍历一个Iterable<Text>
时,如果遍历到一半跳出循环,在重新开一个循环的时候就会发现只循环了剩下的一半数据,像是个堆栈,至于为什么还得看源码,看书 等我看到了再补吧。
至于原因,小弟才疏学浅,尚不得知,不过应该和引用到了同一个地址的问题原理差不多吧。不当之处,请指正!谢谢