2012-02-17 37 views
3

我正在处理HBase中存储的大量数据。存储在我的列中的许多值实际上是数据的“向量” - 多个值。我开始处理存储多个值的方式是通过ByteBuffer。由于我知道存储在我的列族的每一列中的数据类型,因此我编写了一系列扩展基类的类,这些类包装在ByteBuffer中,并为我提供了一套简单的方法来读取各个值以及将附加值附加到结束。我已经独立于我的HBase项目测试过这个类,它按预期工作。额外的字节附加到由HBase返回的值TableMapper

为了更新我的数据库(每个更新中几乎每行都更新),我使用一个TableMapper mapreduce作业遍历数据库中的每一行。我的每个映射器(在我的集群中有六个映射器)将整个更新文件(很少超过50MB)加载到内存中,然后在迭代它时更新每个行ID。

我遇到的问题是每当我从Result对象中取出一个数据值时,它有4个字节附加到它的末尾。这使我的更新变得困难,因为我不确定是否期望这个“填充”每次多出4个字节,或者是否可以扩大到更大/更小。由于我将此加载到我的ByteBuffer包装器中,所以没有填充是很重要的,因为那样会在我附加额外的数据点时导致数据中存在空隙,这将使得以后无法读取它们而出错。

我写了一个测试,通过创建一个测试表和类来确认我的假设。该表每列只有一个数据点(单个双 - 我已确认进入的字节的长度是8),并且我写下了以下代码以检索并检查它。

HTable table = new HTable("test"); 
byte[] rowId = Bytes.toBytes("myid"); 
Get get = new Get(rowId); 
byte[] columnFamily = Bytes.toBytes("data"); 
byte[] column = Bytes.toBytes("column"); 
get.addColumn(columnFamily, column); 
Result = table.get(get); 
byte[] value = result.value(); 
System.out.printlin("Value size: " + value.length); 
double doubleVal = Bytes.toDouble(value); 
System.out.println("Fetch yielded: " + doubleVal); 
byte[] test = new byte[8]; 
for (int i = 0; i < value.length - 4; i++) 
    blah[i] = value[i]; 
double dval = Bytes.toDouble(test); 
System.out.println("dval: " + dval); 
table.close() 

导致:

Value size: 12 
Fetch yielded: 0.3652 
dval: 0.3652 

这些值是可以预期的。

有关如何解决此问题的任何想法?我意识到像Avro这样的序列化引擎的存在,但我试图暂时避免使用它们,我的数据非常直观,以至于我觉得我不应该这样做。

编辑:我继续前进,截断我的数据的最大公倍数我的数据类型的大小。根据我的经验,这些额外的字节专门添加到我的byte[]阵列的末尾。我已经做了几个能够以相当干净的方式自动处理这个问题的类,但我仍然好奇为什么会发生这种情况。

回答

2

使用的MapReduce到HBase的导入数据时,我也有类似的问题。由于以下代码,我的rowkeys上会附加垃圾字节:

public class MyReducer extends TableReducer<Text, CustomWritable, Text> { 

    protected void reduce(Text key, Iterable<CustomWritable> values, Context context) throws IOException, InterruptedException { 
     // only get first value for the example 
     CustomWritable value = values.iterator().next(); 
     Put put = new Put(key.getBytes()); 
     put.add(columnFamily, columnName, value.getBytes()); 
     context.write(outputKey, put); 
     } 
    } 

问题在于文本。getBytes()从后端返回实际的字节数组(见Text),而Text对象被MapReduce框架重用。因此,字节数组将具有它保存的先前值的垃圾字符。这个变化对我来说是固定的:

Put put = new Put(Arrays.copyOf(key.getBytes(), key.getLength())); 

如果你在某处使用Text作为你的值类型,它可能会做同样的事情。