Hbase-API-batch、扫描、缓存和批量处理,
批量处理操作:可以批量处理跨多行的不同操作
许多基于列表的操作,如delete、get的列表操作都是基于batch(新手推荐)方法实现的
//HTable
void batch(List<Row> actions,object[] resultts) throws Ex
//results将于actions结果一一对应(类似map)
Object[] bacth(List<Row> actions)
//其中的Row类是put、get、delete的父
使用同样的父类允许在列表中实现多态,
results中存有输入操作返回的一个匹配结果
空的result实例返回null
get返回KeyValue实例
错误列族操作返回一个异常供参考
最好不要将对同一行的put和delete操作放在同一个批处理请求中。因为为保证最好的性能,操作的实现顺序可能不同,这样会产生不可预料的结果,还有就是资源竞争,会使结果波动。
批量处理应用
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf,"testtable");
List<Row> batch = new ArrayList<>();
Put put = new Put(Bytes.toBytes("row1"));
put.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual3"), Bytes.toBytes("testdata3"));
batch.add(put);
Delete delete1 = new Delete(Bytes.toBytes("row1"));
delete1.deleteColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("qual2"));
batch.add(delete1);
Get get = new Get(Bytes.toBytes("row1"));
get.addColumn(Bytes.toBytes("bug"), Bytes.toBytes("qual3"));
batch.add(get);
Object[] results = new Object[batch.size()];
try{
table.batch(batch, results);
}catch(Exception e){
System.err.println(e);
}
for(int i=0;i<results.length;i++){
System.out.println("result"+"["+i+"]:"+results[i]);
}
//在检查结果之前,操作都会执行,异常放在最后抛出
//delete和put操作对应为NONE
使用batch时,put实例不会被客户端写入缓冲区操作,batch是同步的,会把操作直接发送到服务器与put调用不同(慎重)
1.void batch(List<Row> actions,object[] resultts) throws Ex
2.Object[] bacth(List<Row> actions)
//1方法可以让用户访问部分结果
//2方法如果抛出异常。不会有任何结果,因为新结果数组返回之前。控制流就中断了
//1会先向数组填充数据,然后抛出异常,2只返回客户端异常,不能访问程序执行的部分结果
批量操作可以感知暂时性错误,NotServingRegionException(region被移动),会多次重试这个操作
配置hbase.client.rettries.number项:修改重试次数
扫描
扫描(scan)技术,类似于数据库系统中的游标,利用hbase提供的底层顺序存储的数据结构
扫描操作和get类似,工作方式类似于迭代器(所以用户无需调用scan方法创建实例),使用HTable的getScanner方法,返回真正的扫描器Resultscanner实例的同时,也可使用它迭代数据
Resultscanner getScanner(Scan scan)
Resultscanner getScanner(byte[] family)
Resultscanner getScanner(byte[] family,byte[] qualifier)
//后两个方法隐式的创建了Scan实例
//Scan类的构造器
Scan()
Scan(byte[] startRow,Filter filter)
Scan(byte[] startRow)
Scan(byte[] startRow,byte[] stopRow)
//[startRow,stopRow)
//Scan的限制条件,限制返回内容
Scan addFamily(byte[] family)
Scan addColumn(byte[] family,byte[] qualifier)
扫描器不用提供精确的起终行。扫描会匹配相等或大于给定行的行键,如果没有显示指定起始行,会从表的起始位置开始读取
扫描会遇到终止行或者大于终止行时,停止,如果没有显示指定终止行,会扫描到表尾
可选参数Filter,可指向Filter实例
通常Scan实例由空白构造器(扫描全表)构造,但是其可选参数都有对应的getter和setter
Scan setStartRow(byte[] startRow)
Scan setVersions()
boolean hasFilter
...
HBase的数据是按照列族存储的,如果扫描不读取某个列族,那么整个列族文件就都不会被读取,这是列式存储的优势
设置好Scan过后,可调用HTable的getScanner()获得用于检索的数据
ResultScanner类
扫描操作不会通过一次RPC请求返回所有匹配的行,而是以行为单位进行返回
行数目会很大
一次请求中发送大量数据,会占用大量系统资源耗时间
ResultScanner把扫描操作转换为类似get操作,它将每一行数据封装成一个Result实例,并将Result实例放入一个迭代器
Result next()//返回单个result实例
Result[] next(int nbRows)//返回多个实例
//盗用close方法释放扫描控制的资源
扫描器租约
要确保尽早释放扫描器实例,一个打开的扫描器会占用很多的服务器端资源,积累多了会占用大量的堆空间。
应把close方法放在try/catch中,确保在迭代过程中异常也能关闭资源
和行锁一样,扫描器也有租约超时机制,保护其不被失效的客户端阻塞太久,
//配置修改超时时间
hbase.regionserver.lease.period
<value>120000</>//取值适当
Scan scan = new Scan();
scan.addColumn(Bytes.tobytes("colfam1"),Bytes.tobytes("col-5"));
scan.setStartRow(Bytes.tobytes("row-10"));
ResultScanner scanner = table.getScanner(scan);
for(result res : scanner){
System.out.println(res)
}
scanner.close();
缓存和批量处理
每个next()调用都会产生一个单独的RPC请求,使用next(int nbRows)也是一样
单元格数据较小时,性能不会很好(参见,客户端的写缓冲区)
如果一次RPC请求可以获取多行数据,会更有意义,这由扫描器缓存(scanner caching)实现,默认这个是关闭的
可在两个层面打开它。1.表层,所有的扫描实例的缓存都会生效
2.扫描层(优先级最高),这样只影响当前的扫描实例
扫描层可以覆盖表层。如不使用层次,则使用默认配置文件
//表层,HTable
void setScannerCaching(int scannerCaching)//设置大小
int getScannerCaching()//获取大小
//也可修改集群配置
hbase.client.scanner.caching默认为1
//也可以在Scan类设置扫描级别
void setCaching(int caching)
int getCaching()
这些方法都可以控制RPC调用取回的行数,两种next方法都受影响
在使用中需要为少量RPC请求次数和客户端以及服务器端的内存消耗找平衡点。(注意不要让一个RPC请求的数据超过其堆大小)