HBase基础知识(2):CRUD操作之get方法

HTable类中提供了get()方法,同时还有与之对应的Get类。get方法分为两类:一类是一次获取一行数据;另一类是一次获取多行数据。

单行get

这种方法可以从HBase中获取一个特定的值:

Result get(Get get) throws IOException

与put()方法对应Put类相似,get()方法也有对应的Get类,此外还有一个相似之处,那就是在使用下面的方法构造Get实例时,与也需要设置行键:

Get(byte [] row)
Get(byte [] row,RowLock rowLock)

虽然一次get()操作只能读取一行数据,但不会限制一行当中取多少列或者多少单元格。
这两个Get实例都通过row参数指定了要获取的行,其中第二个Get实例还增加了一个可选的rowLock参数,允许用户设置行锁。
与put操作一样,用户有许多方法可用,可以通过多种标准筛选目标数据,也可以指定精确的坐标获取某个单元格的数据:

Get addFamily(byte [] family)
Get addColumn(byte [] family,byte [] qualifier)
Get setTimeRange(long minStamp,long maxStamp) throws IOException
Get setTimeStamp(long timestamp)
Get setMaxVersions()
Get setMaxVersions(int maxVersions)throws IOException

addFamily()方法限制get请求只能取得一个指定的列族,要取得多次调用。addColumn()方法的使用与之类似,用户通过它可以指定get取得哪一列的数据,从而进一步缩小地址空间。有一些方法允许用户设定要获取的数据的时间戳,或者通过设定一个时间段来取得时间戳属于该时间段内的数据。
最后,如果用户没有设定时间戳的话,也有方法允许用户设定要获取的数据的版本数目。默认情况下,版本数为1,即get()方法允许用户设定要获取的数据的版本数目。默认情况下,版本数为1,即get()请求返回最新的匹配版本。如果有疑问,可以使用getMaxVersions()来检查这个Get实例所要获取的最大版本数。不带参数的setMaxVersions()方法会把要取出的最大版本数设为Integer.MAX_VALUE,这是用户在列族描述符中可配置的最大版本数,此时系统会返回这个单元格中所有的版本,换句话说,此时系统会返回用户在列族中已配置的最大版本数以内的所有数据。

方法 描述
getRow() 返回创建Get实例时指定的行键
getRowLock() 返回当前Get实例的RowLock实例
getLockId() 返回创建时指定rowLock的锁ID,如果没有指定返回-1L
getTimeRange() 返回指定的Get实例的时间戳范围。注意,Get类中已经没有getTimeStamp()方法了,因为API会在内部将setTimeStamp()赋的值转换成TimeRange实例,设定给定时间戳的最大值和最小值
setFilter()/getFilter() 用户可以使用一个特定的过滤器实例,通过多种规则和条件来筛选列和单元格。使用这些方法用户可以设定或查看Get实例的过滤器成员
setCacheBlocks()/getCacheBlocks() 每个HBase的region服务器都有一个块缓存来有效地保存最近存取过的数据,并以此来加速之后的相邻信息的读取。不过在某些情况下,例如完全随机读取时,最好能避免这种机制带来的扰动。这些方法能够控制当次读取的块缓存机制是否启效
numFamilies() 快捷地获取列族FamilyMap大小的方法,包括用addFamily()方法和addColumn()方法添加的列族
hasFamilies() 检查列族或列是否存在于当前的Get实例中
familySet()/getFamilyMap() 这些方法能让用户直接访问addFamily()和addColumn()添加的列族和列。FamilyMap列族中键是列族的名称,键对应的值是指定列族的限定符列表。familySet()方法返回一个所有已存储列族的Set,即一个只包含列族名的集合


以前提到过,HBase为用户提供了Bytes这个工具类,该类有许多可以把Java的常用数据类型转化为byte[]数组的静态方法。同时,它也可以做一些反向转化的工作:例如当用户从HBase中获取一行数据时,可以用Bytes对应的方法把byte[]内容转化为Java的数据类型。

static String toString(byte[] b)
static boolean toBoolean(byte[] b)
static long toLong(byte[] bytes)
static float toFloat(byte[] bytes)
static int toInt(byte[] bytes)

下边代码展示了从HBase中获取数据的完整过程。

Configuration conf = HBaseConfiguration.create();

HTable table = new HTable(conf, "testtable");

Get get = new Get(Bytes.toBytes("row1"));
get.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual1"));
Result result = table.get(get);
byte[] val = result.getValue(Bytes.toBytes("colfam1"),
                             Bytes.toBytes("qual1"));
System.out.println("Values:" + Bytes.toString(val));

Result类

当用户使用get()方法获取数据时,HBase返回的结果包含所有匹配的单元格数据,这些数据将被封装在一个Result实例中返回给用户。用它提供的方法,可以从服务器端获取匹配指定行的特定返回值,这些值包括列族、列限定符和时间戳等。提供的方法如下

byte[] getValue(byte[] family,byte[] qualifier)
byte [] value()
byte [] getRow()
int size()
boolean isEmpty()
KeyValue[] raw()
List<KeyValue> list()

getValue()方法允许用户取得一个HBase中存储的特定单元格的值。因为该方法不能指定时间戳,所以用户只能获取数据最新的版本。value()方法的使用更简单,它会返回第一个列对应的最新单元格的值。因为列在服务器端是按字典存储的,所以会返回名称(包括列族和列限定符)排在首位的那一列的值。
getRow()方法:它返回创建Get类当前实例使用的行键。size()方法返回服务器端返回值中键值对(KeyValue实例)的数目。用户可以使用size()方法或者isEmpty()方法查看键值对的数目是否大于0,这样可以检查服务器端是否找到了与查询相对应的结果。
raw()方法返回原始的底层KeyValue的数据结构。具体来说,是基于当前的Result实例返回KeyValue实例的数组。list()调用则把raw()中返回的数组转化为一个list()实例,并返回给用户,创建的List实例由原始返回结果中的KeyValue数组成员组成,用户可以方便地地带使用数据。
raw()方法返回的数组已经按照字典序排列,排列时考虑了KeyValue实例的所有坐标。先按列族排序,列族内再按列限定符排序,此后再按时间戳排序,最后按类型排序
另外还有一些面向列的存取函数如下:

List<KeyValue> getColumn(byte [] family, byte [] qualifier)
KeyValue getColumnLatest(byte []family, byte[] qualifire)
boolean containColumn(byte[] family, byte [] qualifier)

返回值中的版本数取决于用户调用get()方法之前,创建Get()实例时设置的最大版本数,默认是1。换句话说,getColum()返回的列表中包括0(当本行没有该列值时)或1个条目,这一条目时该列最新版本的值。如果用户指定了一个比默认值1大的版本数,返回的列表中就可能会有多个条目。
getColumnLatest()方法返回对应列的最新版本值,不过与getValue()不同,它不返回值的原始字节数组,而是返回一个KeyValue实例。如果用户需要的不仅仅是数据,那么这个方法将会非常有用。containsColumn()检查返回值中是否有对应的列。

不使用限定符就意味着这一列没有标签。当查询表时,例如,用户通过命令行查询时,需要自己明白数据所表示的具体含义。可能只有一种情况能用到空限定符:即一个列族下只有一列,同时列族名就能够表示数据的含义及目的。
下面试第三类取值函数,以映射形式返回结果:

NavigableMap<byte [], NavigableMap<byte[], NavigableMap<Long, byte[]>>>getMap()
NavigableMap<byte[], byte[] >> getNoVersionMap()
NavigableMap<byte[], byte[]> getFamilyMap(byte[] family)

最常用的方法是getMap(),它把所有get()请求返回的内容都装入一个Java的Map类实例,这样用户可以使用该方法遍历所有结果。getNoVersionMap()与getMap()形式上相似,不过它只返回每个列的最新版本。getFamilyMap()允许用户指定一个特定的列族,返回这次结果中这个列族下的所有版本。
不论用户使用什么方法获取Result中的数据,都不会产生额外的性能和资源消耗,因为这些数据都已经通过网络从服务端传输到了客户端。

Get列表

使用列表参数的get()方法与使用列表参数的put()方法对应,用户可以用一次请求获取多行数据。它允许用户快速高效的从远程服务器获取相关的或完全随机的多行数据。
API提供的方法签名如下:

Result[] get(List<Get> gets)throws IOException

下面是使用Get实例的列表从HBase中获取数据

Configuration conf = HBaseConfiguration.create();

HTable table = new HTable(conf, "testtable");

byte[] cf1 = Bytes.toBytes("colfam1");
byte[] qf1 = Bytes.toBytes("qual1");
byte[] qf2 = Bytes.toBytes("qual2");
byte[] row1 = Bytes.toBytes("row1");
byte[] row2 = Bytes.toBytes("row2");

List<Get> gets = new ArrayList<Get>();

Get get1 = new Get(row1);
get1.addColumn(cf1, qf1);
gets.add(get1);
Get get2 = new Get(row2);
get2.addColumn(cf1, qf2);
gets.add(get2);
Get get3 = new Get(row2);
get3.addColumn(cf1, qf2);
gets.add(get3);
Result[] results = table.get(gets);
System.out.println("First iteration…");
for (Result result : results)
{
    String row = Bytes.toString(result.getRow());
    System.out.println("Row:" + row + " ");
    byte[] val = null;
    if (result.containsColumn(cf1, qf1))
    {
        val = result.getValue(cf1, qf1);
        System.out.println("Value:" + Bytes.toString(val));
    }
    if (result.containsColumn(cf1, qf2))
    {
        val = result.getValue(cf1, qf2);
        System.out.println("Value:" + Bytes.toString(val));
    }
}
System.out.println("Second iteration...");
for (Result result : results)
    for (KeyValue kv : result.raw())
    {
        System.out.println("Row:" + Bytes.toString(kv.getRow())
                           + "Value: " + Bytes.toString(kv.getValue()));
    }

}

get()方法要么返回与给定列表大小一致的Result数组,要么抛出一个异常,代码如下

Configuration conf = HBaseConfiguration.create();

HTable table = new HTable(conf, "testtable");

byte[] cf1 = Bytes.toBytes("colfam1");
byte[] qf1 = Bytes.toBytes("qual1");
byte[] qf2 = Bytes.toBytes("qual2");
byte[] row1 = Bytes.toBytes("row1");
byte[] row2 = Bytes.toBytes("row2");

List<Get> gets = new ArrayList<Get>();

Get get1 = new Get(row1);
get1.addColumn(cf1, qf1);
gets.add(get1);
Get get2 = new Get(row2);
get2.addColumn(cf1, qf2);
gets.add(get2);
Get get3 = new Get(row2);
get3.addColumn(cf1, qf2);
gets.add(get3);
Get get4 = new Get(row2);
get4.addColumn(Bytes.toBytes("BOGUS"), qf2);
gets.add(get4);
Result[] results = table.get(gets);
System.out.println("Result count:" + results.length);

获取数据的相关方法

可以和使用HTable的get()方法一样,先创建一个Get类的实例。exists()方法通过RPC验证请求数据是否存在,但不会从远程服务器返回请求的数据,只返回一个布尔值表示这个结果。
exits()方法会引发region服务器端查询数据的操作,包括加载文件块来检查某行或某列是否存在。用户通过这种方法只能避免网络数据传输的开销,不过在需要检查或频繁检查一个比较大的列时,这种方法还是十分使用的。
用户需要指定要查找的行键和列族。指定后者的原因是,HBase是一个列式存储的数据库,不存在没有列的行数据。设定列族之后,服务器端会检查要查找的那一行里是否有任何属于指定列族的列值。

可以从getRowOrBefore()返回的Result实例中得到要查找的行键。这个行键要么与用户设定的行一致,要么刚好是设定行键之前的一行。如果没有匹配的结果,本方法返回null。

下边是使用特殊检索方法代码:

Result result1 = table.getRowOrBefore(Bytes.toBytes("row1"),
                                      Bytes.toBytes("colfam1"));
System.out.println("Found: " + Bytes.toString(result1.getRow()));
Result result2 = table.getRowOrBefore(Bytes.toBytes("row99"),
                                      Bytes.toBytes("colfam1"));
System.out.println("Found: " + Bytes.toString(result2.getRow()));
for (KeyValue kv : result2.raw())
{
    System.out.println("Col: " + Bytes.toString(kv.getFamily()) + "/"
                       + Bytes.toString(kv.getQualifier()) + ",Value"
                       + Bytes.toString(kv.getValue()));
}
Result result3 = table.getRowOrBefore(Bytes.toBytes("abc"),
                                      Bytes.toBytes("colfam1"));
System.out.println("Found: " + result3);

第一次调用找到一个匹配的行,成功返回。第二次调用使用了一个大数字作为后缀来查找表的最后一行。从row-前缀开始,查找到对应的row-2行。最后一个例子要查找abc这一行或者这行之前的一行,不过之前插入数据的前缀是row-,所以abc以及之前的行不存在。因此返回值为null,表示查找失败。
运行结果如下

Found:row1
Found:row2
  Col:colfam1/qual1,Value:val2
  Col:colfam1/qual2,Value:val3
Found:null

posted on 2015-08-25 14:11  爱你一万年123  阅读(542)  评论(0编辑  收藏  举报

导航