本节书摘来异步社区《hbase权威指南》一书中的第3章,第3.2节,作者: 【美】lars george 译者: 代志远 , 刘佳 , 蒋杰 责编: 杨海玲,更多章节内容可以访问云栖社区“异步社区”公众号查看。
数据库的初始基本操作通常被称为crud(create,read,update,delete),具体指增、查、改、删。hbase中有与之相对应的一组操作,随后我们会依次介绍。这些方法都由htable类提供,本章后面将直接引用这个类的方法,不再特别提到这个包含类。
接下来介绍的操作大多都能不言自明,但本书有一些细节需要大家注意。这意味着,对于书中出现的一些重复的模式,我们不会多次赘述。
读者一开始会在一些程序的开头看到import语句,但为了简洁,后续将会省略import语句。同时,一些与主题不太相关的代码部分也会被省略。如有疑问,请到上面的地址中查阅完整源代码。
下面介绍的这组操作可以被分为两类:一类操作用于单行,另一类操作用于多行。鉴于后面有一些内容比较复杂,我们会分开介绍这两类操作。同时,我们还会介绍一些衍生的客户端api特性。
1.单行put
也许你现在最想了解的就是如何向hbase中存储数据,下面就是实现这个功能的调用:
这个方法以单个put或存储在列表中的一组put对象作为输入参数,其中put对象是由以下几个构造函数创建的:
创建put实例时用户需要提供一个行键row,在hbase中每行数据都有唯一的行键(row key)作为标识,跟hbase的大多数数据类型一样,它是一个java的byte[]数组。用户可以按自己的需求来指定每行的行键,且可以参考第9章,其中专门有一节详细讨论了行键的设计(见9.1节)。现在我们假设用户可以随意设置行键,通常情况下,行键的含义与真实场景相关,例如,它的含义可以是一个用户名或者订单号,它的内容可以是简单的数字,也可以是较复杂的uuid②等。
hbase非常友好地为用户提供了一个包含很多静态方法的辅助类,这个类可以把许多java数据类型转换为byte[]数组。例3.1提供了方法的部分清单。
例3.1 bytes类所提供的方法
...
创建put实例之后,就可以向该实例添加数据了,添加数据的方法如下:
每一次调用add()都可以特定地添加一列数据,如果再加一个时间戳选项,就能形成一个数据单元格。注意,当不指定时间戳调用add()方法时,put实例会使用来自构造函数的可选时间戳参数(也称作ts),如果用户在构造put实例时也没有指定时间戳,则时间戳将会由region服务器设定。
系统为一些高级用户提供了keyvalue实例的变种,这里所说的高级用户是指知道怎样检索或创建这个内部类的用户。keyvalue实例代表了一个唯一的数据单元格,类似于一个协调系统,该系统使用行键、列族、列限定符、时间戳指向一个单元格的值,像一个三维立方体系统(其中,时间成为了第三维度)。
获取put实例内部添加的keyvalue实例需要调用与add()相反的方法get():
以上两个方法可以查询用户之前添加的内容,同时将特定单元格的信息转换成keyvalue实例。用户可以选择获取整个列族(column family)的全部数据单元,一个列族中的特定列或是全部数据。后面的getfamilymap()方法可以遍历put实例中每一个可用的keyvalue实例,检查其中包含的详细信息。
图像说明文字每一个keyvalue实例包含其完整地址(行键、列族、列限定符及时间戳)和实际数据。keyvalue是hbase在存储架构中最底层的类。8.2节将会详细介绍相关内容。对于客户端api所用到的keyvalue类中的方法参见3.2.1节。
用户可以采用以下这些方法检查是否存在特定的单元格,而不需要遍历整个集合:
随着以上方法所使用参数的逐步细化,获得的信息也越详细,当找到匹配的列时返回true。第一个方法仅检查一个列是否存在,其他的方法则增加了检查时间戳、限定值的选项。
put类还提供了很多的其他方法,在表3-1中进行了概括。
文字注意,表3-1所列的put类中的那些getter函数仅能够获取用户预先设定的内容,实际应用中很少用到它们,仅当用户在代码的私有方法中准备实现一个put实例,并在其他地方检查其内容时,才会用到它们。
例3.2展示了如何在一个简单的程序里使用上述方法。
文字本章的示例使用了一个非常有限的精确数据集。当读者查看整个源代码时,会注意到源代码使用了一个名叫hbasehelper的内部类。该内部类会创建一个有特定行和列数量的数据测试表。这让我们更容易对比处理前后的差异。
读者可以将代码直接放到本地主机上的独立hbase实例中来测试,也可以放到hbase集群上测试。前言中的“编译示例程序”一节解释了如何编译这些例子。读者可以大胆地修改这部分代码,以便更好地体会各部分功能。
为了清除前一步示例程序执行时产生的数据,示例代码通常会删除前一步执行时所创建的表。如果你在生产集群上运行示例,请先确保表名无冲突。通常示例代码创建的表为testtable,这个名称容易让人联想到表的用途。
例3.2 向hbase插入数据的示例应用
创建所需的配置。
实例化一个新的客户端。
指定一行来创建一个put。
向put中添加一个名为“colfam1:qual1”的列。
向put中添加另一个名为“colfam1:qual2”的列。
将这一行存储到hbase表中。
这个示例代码(几乎)十分完整,并且每一行都进行了解释。以后的示例会逐渐减少样板代码,以便读者能将注意力集中到重要的部分。
通过客户端代码访问配置文件
2.6.7节介绍了hbase客户端应用程序使用的配置文件。应用程序需要通过默认位置(classpath)下的hbase-site.xml文件来获知如何访问集群,此外也可以在代码里指定集群的地址。
无论哪种方式,都需要在代码中使用一个hbaseconfiguration类来处理配置的属性。可以使用该类提供的以下静态方法构建configuration实例:
例3.2中使用了create()来获得configuration实例。第二个方法允许你使用一个已存在的配置,该配置会融合并覆盖hbase默认配置。
当你调用任何一个静态create()方法时,代码会尝试使用当前的java classpath来载入两个配置文件:h<code>`</code>javascript
base-default.xml和hbase-site.xml。
hbase(main):001:0>list
table
testtable
1 row(s) in 0.0400 seconds
hbase(main):002:0>scan 'testtable'
row column+cell
row1 column=colfam1:qual1,timestamp=1294065304642,value=val1
1 row(s) in 0.2050 seconds
hbase(main):001:0> create 'test','cf1
0 row(s) in 0.9810 seconds
hbase(main):002:0> put 'test','row1','cf1','val1'
0 row(s)in 0.0720 seconds
hbase(main):003:0> put 'test','row1','cf1','val2'
0 row(s) in 0.0520 seconds
hbase(main):004:0> scan 'test'
row column+cell
row1 column=cf1:,timestamp=1297853125623,value=val2
1 row(s) in 0.0790 seconds
hbase(main):005:0> scan 'test',{ versions => 3 }
row1 column=cf1:,timestamp=1297853122412,value=val1
1 row(s) in 0.0640 seconds
keyvalue(byte[] row, int roffset, int rlength,
byte[] family, int foffset, int flength, byte[] qualifier, int qoffset,
int qlength, long timestamp, type type, byte[] value, int voffset,
int vlength)
byte[] getbuffer()
int getoffset()
int getlength()
byte [] getrow()
byte [] getkey()
treeset< keyvalue> set =
new treeset< keyvalue>(keyvalue.comparator)
string tostring()
< row-key>/< family>:< qualifier>/< version>/< type>/< value-length>
void setautoflush(boolean autoflush)
boolean isautoflush()
table.setautoflush(false)
void flushcommits() throws ioexception
long getwritebuffersize()
void setwritebuffersize(long writebuffersize) throws ioexception
< property>
< name>hbase.client.write.buffer< /name>
< value>20971520< /value>
< /property>
htable table = new htable(conf,"testtable");
system.out.println("auto flush: " + table.isautoflush());
table.setautoflush(false);
put put1 = new put(bytes.tobytes("row1"));
put1.add(bytes.tobytes("colfam1"),bytes.tobytes("qual1"),
bytes.tobytes("val1"));
table.put(put1);
put put2 = new put(bytes.tobytes("row2"));
put2.add(bytes.tobytes("colfam1"),bytes.tobytes("qual1"),
bytes.tobytes("val2"));
table.put(put2);
put put3 = new put(bytes.tobytes("row3"));
put3.add(bytes.tobytes("colfam1"),bytes.tobytes("qual1"),
bytes.tobytes("val3"));
table.put(put3);
get get = new get(bytes.tobytes("row1"));
result res1 = table.get(get);
system.out.println("result: " + res1);
table.flushcommits();
result res2 = table.get(get);
system.out.println("result: " + res2);
auto flush: true
result: keyvalues=none
result: keyvalues={row1/colfam1:qual1/1300267114099/put/vlen=4}
void put(list puts)throws ioexception
list< put> puts = new arraylist< put>();
puts.add(put1);
puts.add(put2);
put put3 = new put(bytes.tobytes("row2"));
put3.add(bytes.tobytes("colfam1"),bytes.tobytes("qual2"),
puts.add(put3);
table.put(puts);
hbase(main):001:0>scan 'testtable'
row column+cell
row1 column=colfam1:qual1,timestamp=1300108258094,value=val1
row2 column=colfam1:qual1,timestamp=1300108258094,value=val2
row2 column=colfam1:qual2,timestamp=1300108258098,value=val3
2 row(s)in 0.1590 seconds
put2.add(bytes.tobytes("bogus"),bytes.tobytes("qual1"),
org.apache.hadoop.hbase.client.retriesexhaustedwithdetailsexception:
failed 1 action: nosuchcolumnfamilyexception: 1 time,
servers with issues: 10.0.0.57:51640,
row1 column=colfam1:qual1,timestamp=1300108925848,value=val1
row2 column=colfam1:qual2,timestamp=1300108925848,value=val3
2 row(s) in 0.0640 seconds
put put4 = new put(bytes.tobytes("row2"));
puts.add(put4);
try {
table.put(puts);
} catch(exception e){
system.err.println("error: " + e);
}
error: java.lang.illegalargumentexception: no columns to insert
exception in thread "main"
table.flushcommits()
boolean checkandput(byte[] row,byte[] family,byte[] qualifier,
byte[] value,put put) throws ioexception
boolean res1 = table.checkandput(bytes.tobytes("row1"),
bytes.tobytes("colfam1"),bytes.tobytes("qual1"),null,put1);
system.out.println("put applied: " + res1);
boolean res2 = table.checkandput(bytes.tobytes("row1"),
system.out.println("put applied: " + res2);
put put2 = new put(bytes.tobytes("row1"));
put2.add(bytes.tobytes("colfam1"),bytes.tobytes("qual2"),
bytes.tobytes("val2"));
boolean res3 = table.checkandput(bytes.tobytes("row1"),
bytes.tobytes("colfam1"),bytes.tobytes("qual1"),
bytes.tobytes("val1"),put2);
system.out.println("put applied: " + res3);
bytes.tobytes("val3"));
boolean res4 = table.checkandput(bytes.tobytes("row1"),
bytes.tobytes("val1"),put3);
system.out.println("put applied: " + res4);⓫
exception in thread "main" org.apache.hadoop.hbase.donotretryioexception:
action's getrow must match the passed row
result get(get get) throws ioexception
get(byte[] row)
get(byte[] row,rowlock rowlock)
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
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)
configuration conf = hbaseconfiguration.create();
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("value: " + bytes.tostring(val));
value: val1
byte[] getvalue(byte[] family,byte[] qualifier)
byte[] value()
byte[] getrow()
int size()
boolean isempty()
keyvalue[] raw()
list list()
list getcolumn(byte[] family,byte[] qualifier)
keyvalue getcolumnlatest(byte[] family,byte[] qualifier)
boolean containscolumn(byte[] family,byte[] qualifier)
navigablemap< byte[],navigablemap< byte[],
navigablemap< long,byte[]>>> getmap()
navigablemap< byte[],
navigablemap< byte[],byte[]>> getnoversionmap()
navigablemap< byte[],byte[]> getfamilymap(byte[] family)
keyvalues={row-2/colfam1:col-5/1300802024293/put/vlen=7,
row-2/colfam2:col-33/1300802024325/put/vlen=8}
keyvalues=none
result[] get(list< get> gets) throws ioexception
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,qf1);
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.print("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(keyvalue kv : result.raw()){
system.out.println("row: " + bytes.tostring(kv.getrow())+
" value: " + bytes.tostring(kv.getvalue()));
}
first iteration...
row: row1 value: val1
row: row2 value: val2
row: row2 value: val3
second iteration...
get get4 = new get(row2);
get4.addcolumn(bytes.tobytes("bogus"),qf2);
gets.add(get4);
system.out.println("result count: " + results.length);
failed 1 action: nosuchcolumnfamilyexception: 1 time,
servers with issues: 10.0.0.57:51640,
boolean exists(get get)throws ioexception
result getroworbefore(byte[] row,byte[] family) throws ioexception
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"),
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"),
system.out.println("found: " + result3);
found: row1
found: row2
col: colfam1/qual1,value: val2
col: colfam1/qual2,value: val3
found: null
delete(byte[] row)
delete(byte[] row,long timestamp,rowlock rowlock)
delete deletefamily(byte[] family)
delete deletefamily(byte[] family,long timestamp)
delete deletecolumns(byte[] family,byte[] qualifier)
delete deletecolumns(byte[] family,byte[] qualifier,long timestamp)
delete deletecolumn(byte[] family,byte[] qualifier)
delete deletecolumn(byte[] family,byte[] qualifier,long timestamp)
void settimestamp(long timestamp)
delete delete = new delete(bytes.tobytes("row1"));
delete.settimestamp(1);
delete.deletecolumn(bytes.tobytes("colfam1"),bytes.tobytes("qual1"),1);
delete.deletecolumns(bytes.tobytes("colfam2"),bytes.tobytes("qual1"));
delete.deletecolumns(bytes.tobytes("colfam2"),bytes.tobytes("qual3"),15);
delete.deletefamily(bytes.tobytes("colfam3"));
delete.deletefamily(bytes.tobytes("colfam3"),3);
table.delete(delete);
table.close();
void delete(list deletes) throws ioexception
list< delete> deletes = new arraylist< delete>();
delete delete1 = new delete(bytes.tobytes("row1"));
delete1.settimestamp(4);
deletes.add(delete1);
delete delete2 = new delete(bytes.tobytes("row2"));
delete2.deletecolumn(bytes.tobytes("colfam1"),bytes.tobytes("qual1"));
delete2.deletecolumns(bytes.tobytes("colfam2"),bytes.tobytes("qual3"),5);
deletes.add(delete2);
delete delete3 = new delete(bytes.tobytes("row3"));
delete3.deletefamily(bytes.tobytes("colfam1"));
delete3.deletefamily(bytes.tobytes("colfam2"),3);
deletes.add(delete3);
table.delete(deletes);
before delete call...
kv: row1/colfam1:qual1/2/put/vlen=4,value: val2
kv: row1/colfam1:qual1/1/put/vlen=4,value: val1
kv: row1/colfam1:qual2/4/put/vlen=4,value: val4
kv: row1/colfam1:qual2/3/put/vlen=4,value: val3
kv: row1/colfam1:qual3/6/put/vlen=4,value: val6
kv: row1/colfam1:qual3/5/put/vlen=4,value: val5
kv: row1/colfam2:qual1/2/put/vlen=4,value: val2
kv: row1/colfam2:qual1/1/put/vlen=4,value: val1
kv: row1/colfam2:qual2/4/put/vlen=4,value: val4
kv: row1/colfam2:qual2/3/put/vlen=4,value: val3
kv: row1/colfam2:qual3/6/put/vlen=4,value: val6
kv: row1/colfam2:qual3/5/put/vlen=4,value: val5
kv: row2/colfam1:qual1/2/put/vlen=4,value: val2
kv: row2/colfam1:qual1/1/put/vlen=4,value: val1
kv: row2/colfam1:qual2/4/put/vlen=4,value: val4
kv: row2/colfam1:qual2/3/put/vlen=4,value: val3
kv: row2/colfam1:qual3/6/put/vlen=4,value: val6
kv: row2/colfam1:qual3/5/put/vlen=4,value: val5
kv: row2/colfam2:qual1/2/put/vlen=4,value: val2
kv: row2/colfam2:qual1/1/put/vlen=4,value: val1
kv: row2/colfam2:qual2/4/put/vlen=4,value: val4
kv: row2/colfam2:qual2/3/put/vlen=4,value: val3
kv: row2/colfam2:qual3/6/put/vlen=4,value: val6
kv: row2/colfam2:qual3/5/put/vlen=4,value: val5
kv: row3/colfam1:qual1/2/put/vlen=4,value: val2
kv: row3/colfam1:qual1/1/put/vlen=4,value: val1
kv: row3/colfam1:qual2/4/put/vlen=4,value: val4
kv: row3/colfam1:qual2/3/put/vlen=4,value: val3
kv: row3/colfam1:qual3/6/put/vlen=4,value: val6
kv: row3/colfam1:qual3/5/put/vlen=4,value: val5
kv: row3/colfam2:qual1/2/put/vlen=4,value: val2
kv: row3/colfam2:qual1/1/put/vlen=4,value: val1
kv: row3/colfam2:qual2/4/put/vlen=4,value: val4
kv: row3/colfam2:qual2/3/put/vlen=4,value: val3
kv: row3/colfam2:qual3/6/put/vlen=4,value: val6
kv: row3/colfam2:qual3/5/put/vlen=4,value: val5
after delete call...
system.out.println("kv: " + kv.tostring() +
",value: " + bytes.tostring(kv.getvalue()))
delete delete4 = new delete(bytes.tobytes("row2"));
delete4.deletecolumn(bytes.tobytes("bogus"),bytes.tobytes("qual1"));
deletes.add(delete4);
table.delete(deletes);
} catch(exception e){
system.err.println("error: " + e);
system.out.println("deletes length: " + deletes.size());
for(delete delete : deletes){
system.out.println(delete);
error: org.apache.hadoop.hbase.client.retriesexhaustedwithdetailsexception:
servers with issues: 10.0.0.43:59057,
deletes length: 1
row=row2, ts=9223372036854775807,families={(family=bogus,keyvalues=\
(row2/bogus:qual1/9223372036854775807/delete/vlen=0)}
boolean checkanddelete(byte[] row,byte[] family,byte[] qualifier,
byte[] value,delete delete) throws ioexception
delete1.deletecolumns(bytes.tobytes("colfam1"),bytes.tobytes("qual3"));
boolean res1 = table.checkanddelete(bytes.tobytes("row1"),
bytes.tobytes("colfam2"),bytes.tobytes("qual3"),null,delete1);
system.out.println("delete successful: " + res1);
delete delete2 = new delete(bytes.tobytes("row1"));
delete2.deletecolumns(bytes.tobytes("colfam2"),bytes.tobytes("qual3"));
table.delete(delete2);
boolean res2 = table.checkanddelete(bytes.tobytes("row1"),
system.out.println("delete successful: " + res2);
delete delete3 = new delete(bytes.tobytes("row2"));
try{
boolean res4 = table.checkanddelete(bytes.tobytes("row1"),
bytes.tobytes("colfam1"),bytes.tobytes("qual1"),
bytes.tobytes("val1"),delete3);
system.out.println("delete successful: " + res4);
delete successful: false
delete successful: true
error: org.apache.hadoop.hbase.donotretryioexception:
org.apache.hadoop.hbase.donotretryioexception:
action's getrow must match the passed row