4

为什么可写数据类型应该是可变的?在 Map、Combine、Shuffle 或 Reduce 过程中使用 Text(相对于 String)作为 Key/Value 的数据类型有哪些优势?

谢谢和问候, 拉贾

4

2 回答 2

4

你不能选择,这些数据类型必须是可变的。

原因是序列化机制。让我们看一下代码:

// version 1.x MapRunner#run()
K1 key = input.createKey();
V1 value = input.createValue();

while (input.next(key, value)) {
   // map pair to output
   mapper.map(key, value, output, reporter);
   ...

所以我们再次重用相同的键/值对实例。为什么?我不知道当时的设计决策,但我认为这是为了减少垃圾对象的数量。请注意,Hadoop 已经很老了,当时垃圾收集器的效率不如今天,但是即使在今天,如果您映射数十亿个对象并将它们作为垃圾直接扔掉,它在运行时也会有很大的不同。

您不能使类型真正不可变的真正原因是您不能将Writable字段声明为final. 让我们做一个简单的例子IntWritable

public class IntWritable implements WritableComparable {
  private int value;

  public IntWritable() {}

  public IntWritable(int value) { set(value); }
...

如果你让它不可变,它肯定不再适用于序列化过程,因为你需要定义valuefinal。这是行不通的,因为键和值是在运行时通过反射实例化的。这需要一个默认构造函数,因此InputFormat无法猜测填充最终数据字段所需的参数。因此,重用实例的整个概念显然与不变性的概念相矛盾。

但是,您应该问问自己,不可变键/值在 Map/Reduce 中应该有什么样的好处。在 Joshua Bloch 的《Effective Java》第 15 条中,他指出不可变类更易于设计、实现和使用。他是对的,因为 Hadoop 的 reducer 是可变性最糟糕的例子:

void reduce(IntWritable key, Iterable<Text> values, Context context) ...

可迭代对象中的每个值都指向same共享对象。因此,如果将他们的值缓冲到一个普通的集合中,很多人会感到困惑,并问自己为什么它总是保留相同的值。

最后,它归结为性能(cpu 和内存——想象一下单个键的数十亿个值对象必须驻留在 RAM 中)与简单性之间的权衡。

于 2013-10-29T09:26:44.847 回答
2

简单地说,原因Writable不能ImmutablereadFields(DataInput). Writable反序列化Hadoop实例以使用默认(无参数)构造函数创建实例并调用readFields解析值的方式。由于没有在构造中分配值,因此对象必须是可变的。

于 2013-10-29T10:31:38.123 回答