HBase RowTranslations以替换行中的所有列

HBase RowTranslations以替换行中的所有列,hbase,atomicity,Hbase,Atomicity,我有一个HBase(v0.94.7)表,其中有一个单列族,随着时间的推移,列会添加到该表中。这些列被命名为它们创建的时间戳,因此,除非我查询该行,否则我不知道它有哪些列 现在给定一行,我希望原子地删除此列族的所有现有列,并添加一组新的列和值 因此,我想到使用HBase,比如: 但这段代码最终只是删除了列族,而没有添加新列。这种行为是预期的吗 如果是这样的话,那么我如何实现用一组新列以原子方式替换列族的所有列的目标呢 下面是一个相同的测试用例: import junit.framework.Ass

我有一个HBase(v0.94.7)表,其中有一个单列族,随着时间的推移,列会添加到该表中。这些列被命名为它们创建的时间戳,因此,除非我查询该行,否则我不知道它有哪些列

现在给定一行,我希望原子地删除此列族的所有现有列,并添加一组新的列和值

因此,我想到使用HBase,比如:

但这段代码最终只是删除了列族,而没有添加新列。这种行为是预期的吗

如果是这样的话,那么我如何实现用一组新列以原子方式替换列族的所有列的目标呢

下面是一个相同的测试用例:

import junit.framework.Assert;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableExistsException;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;

import java.util.NavigableMap;

public class TestHBaseRowMutations {
    static String tableName = "nnn";
    static byte[] cf1 = Bytes.toBytes("cf1");
    static byte[] row = Bytes.toBytes("r1");
    static HTablePool hTablePool;

    @BeforeClass
    public static void beforeClass() throws Exception {
        Configuration config = HBaseConfiguration.create();
        hTablePool = new HTablePool(config, Integer.MAX_VALUE);
        HBaseAdmin admin = new HBaseAdmin(config);
        HTableDescriptor tableDescriptor = new HTableDescriptor(tableName);
        tableDescriptor.addFamily(new HColumnDescriptor(cf1));
        try {
            admin.createTable(tableDescriptor);
        } catch (TableExistsException ignored){}
    }

    @Before
    public void before() throws Exception {
        HTableInterface table = hTablePool.getTable(tableName);
        try {
            Delete delete = new Delete(row);
            table.delete(delete);
            System.out.println("deleted old row");

            Put put = new Put(row);
            put.add(cf1, Bytes.toBytes("c1"), Bytes.toBytes("v1"));
            put.add(cf1, Bytes.toBytes("c11"), Bytes.toBytes("v11"));
            table.put(put);
            System.out.println("Created row with seed data");
        } finally {
            table.close();
        }
    }


    @Test
    public void testColumnFamilyDeleteRM() throws Exception {
        HTableInterface table = hTablePool.getTable(tableName);
        try {
            RowMutations rm =new RowMutations(row);

            //delete column family cf1
            Delete delete = new Delete(row);
            delete.deleteFamily(cf1);
            rm.add(delete);
            System.out.println("Added delete of cf1 column family to row mutation");

            //add new columns to same column family cf1
            Put put = new Put(row);
            put.add(cf1, Bytes.toBytes("c1"), Bytes.toBytes("new_v1"));
            put.add(cf1, Bytes.toBytes("c11"), Bytes.toBytes("new_v11"));
            rm.add(put);
            System.out.println("Added puts of cf1 column family to row mutation");

            //atomic mutate the row
            table.mutateRow(rm);
            System.out.println("Mutated row");

            //now read the column family cf1 back
            Result result = table.get(new Get(row));
            NavigableMap<byte[], byte[]> familyMap = result.getFamilyMap(cf1);

            //column family cf1 should have 2 columns because of the Put above
            //------Following assert fails as cf1 does not exist anymore, why does cf1 not exist anymore?-------
            Assert.assertNotNull(familyMap);
            Assert.assertEquals(2, familyMap.size());
        } finally {
            table.close();
        }
    }
}
import junit.framework.Assert;
导入org.apache.hadoop.conf.Configuration;
导入org.apache.hadoop.hbase.HBaseConfiguration;
导入org.apache.hadoop.hbase.HColumnDescriptor;
导入org.apache.hadoop.hbase.HTableDescriptor;
导入org.apache.hadoop.hbase.TableExistsException;
导入org.apache.hadoop.hbase.client.*;
导入org.apache.hadoop.hbase.util.Bytes;
导入org.junit.Before;
导入org.junit.BeforeClass;
导入org.junit.Test;
导入java.util.NavigableMap;
公共类testhBaseroMutations{
静态字符串tableName=“nnn”;
静态字节[]cf1=字节.toBytes(“cf1”);
静态字节[]行=Bytes.toBytes(“r1”);
静态HTablePool HTablePool;
@课前
public static void beforeClass()引发异常{
Configuration config=HBaseConfiguration.create();
hTablePool=新的hTablePool(配置,Integer.MAX_值);
HBaseAdmin admin=新的HBaseAdmin(配置);
HTableDescriptor tableDescriptor=新的HTableDescriptor(tableName);
tableDescriptor.addFamily(新的HColumDescriptor(cf1));
试一试{
admin.createTable(tableDescriptor);
}catch(忽略TableExistsException){}
}
@以前
public void before()引发异常{
HTableInterface table=hTablePool.getTable(tableName);
试一试{
删除=新删除(行);
表.删除(删除);
System.out.println(“删除的旧行”);
Put Put=新Put(行);
put.add(cf1,Bytes.toBytes(“c1”),Bytes.toBytes(“v1”);
put.add(cf1,Bytes.toBytes(“c11”),Bytes.toBytes(“v11”);
表.put(put);
System.out.println(“使用种子数据创建的行”);
}最后{
table.close();
}
}
@试验
public void testColumnFamilyDeleteRM()引发异常{
HTableInterface table=hTablePool.getTable(tableName);
试一试{
行突变rm=新的行突变(行);
//删除列族cf1
删除=新删除(行);
删除。删除家庭(cf1);
rm.添加(删除);
System.out.println(“将cf1列族的删除添加到行突变”);
//将新列添加到同一列族cf1
Put Put=新Put(行);
put.add(cf1,Bytes.toBytes(“c1”),Bytes.toBytes(“new_v1”);
put.add(cf1,Bytes.toBytes(“c11”),Bytes.toBytes(“new_v11”);
rm.add(put);
System.out.println(“将cf1列族的puts添加到行突变”);
//原子突变行
表2.mutateRow(rm);
System.out.println(“变异行”);
//现在读回列族cf1
结果=table.get(新的get(行));
NavigableMap familyMap=result.getFamilyMap(cf1);
//由于上述原因,列族cf1应具有2列
//------以下断言失败,因为cf1不再存在,为什么cf1不再存在-------
Assert.assertNotNull(familyMap);
Assert.assertEquals(2,familyMap.size());
}最后{
table.close();
}
}
}

在HBase用户论坛上发布了相同的问题,结果发现这是HBase中的一个bug

预期的行为是,如果一个行变异对某个列族/列/行进行了删除,然后对同一列族/列/行进行了Put,则该Put也应被执行(但目前的情况并非如此)

HBase用户组讨论此问题:

HBase JIRA针对相同的:
这也提供了补丁。

最接近的方法是将Put上的时间戳设置为高于Delete上的时间戳:

long now = System.currentTimeMillis();

Delete delete = new Delete(row);
delete.deleteFamily(cf1, now);

Put put = new Put(row);
put.add(cf1, col1, now + 1);

RowMutations mutations = new RowMutations(row);
mutations.add(delete);
mutations.add(put);

table.mutateRow(mutations);

遗憾的是,这确实意味着时间戳“now”的
get
在该列族中将没有任何内容

当我们尝试执行包含有效Put for ROW1:CF1:Q1:V1和Delete for ROW1:CF2:Q1:V1作为hbase批量操作的行突变列表时,得到了一个要共享的场景,并得出以下错误

java.lang.RuntimeException:java.lang.UnsupportedOperationException: 在多个呼叫中没有行突变;使用mutateRow在 org.apache.hadoop.hbase.client.RpcRetryingCaller.callWithoutRetries(RpcRetryingCaller.java:218) 在 org.apache.hadoop.hbase.client.AsyncProcess$AsyncRequestFutureImpl$SingleServerRequestRunnable.run(AsyncProcess.java:748) 在 Executors$RunnableAdapter.call(Executors.java:511) 在java.util.concurrent.FutureTask.run(FutureTask.java:266)处 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149) 在 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) 运行(Thread.java:748)


为了解决这个问题,我们选择分别执行每个行。欢迎您提出任何建议。

是否您正在删除列族,而不是单个列本身?如上所述,我事先不知道列名,因此是的,我正在使用delete.deleteFamily(cf)删除列族;然后使用put.add(cf,col1,v1);。。。
long now = System.currentTimeMillis();

Delete delete = new Delete(row);
delete.deleteFamily(cf1, now);

Put put = new Put(row);
put.add(cf1, col1, now + 1);

RowMutations mutations = new RowMutations(row);
mutations.add(delete);
mutations.add(put);

table.mutateRow(mutations);