一、介绍
Put、Delete与Get对象都是Row的子类,从该继承关系中我们就可以了解到Get、Delete与Pu对象本身就只能进行单行的操作,HBase客户端还提供了一套能够进行全表扫描的API,方便用户能够快速对整张表进行扫描,以获取想要的结果---scan
二、流程介绍
全表扫描是一种不需要行键值的操作,因此初始化时不需要指定行键值,因此就产生了不同的使用方法
1、不进行Scan对象创建的全表扫描
在该过程中,Htable对象会在扫描请求发送前隐式的创建一个scan对象,然后传递给Hbase服务器集群。
public void scanWithoutInit(String tableName,String family)
{
Configuration conf=init();
try {
HBaseAdmin admin=new HBaseAdmin(conf);
if(!admin.tableExists(tableName))
{
System.err.println("the table "+tableName+" is not exist");
admin.close();
System.exit(1);
}
//创建表连接
HTable table=new HTable(conf, tableName);
//获取全表扫描
ResultScanner resultScanner=table.getScanner(Bytes.toBytes(family));
//对结果进行显示
Iterator<Result> results=resultScanner.iterator();
while(results.hasNext())
{
Result result=results.next();
for(KeyValue kv:result.raw())
{
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
}
}
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
getScanner()方法时,如果不输入指定的scan对象,则需要输入相应的列簇或者列。因此在不进行scan对象创建的扫描中,需要明确指出列簇或者列,如果需要扫描多个列簇时,该方法就无法起到作用了。
2、进行初始化的全表扫描
初始化一个scan对象,然后对该对象进行相应的配置过,通过 getScanner(Scan scan) 函数进行全表扫描。
public void scanWithInit(String tableName)
{
Configuration conf=init();
try {
HBaseAdmin admin=new HBaseAdmin(conf);
if(!admin.tableExists(tableName))
{
System.err.println("the table "+tableName+" is not exist");
admin.close();
System.exit(1);
}
//创建扫描类
Scan scan=new Scan();
scan.setStartRow(Bytes.toBytes("row-1"));
scan.setStopRow(Bytes.toBytes("row-9"));
//创建表连接
HTable table=new HTable(conf, tableName);
ResultScanner rs=table.getScanner(scan);
Result result;
while((result=rs.next())!=null)
{
KeyValue[] kvs=result.raw();
for(KeyValue kv:kvs)
{
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
}
}
rs.close();
table.close();
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
}
在上段代码中,使用了setStartRow() 与 setStopRow() 两个函数进行调优。Scan有多个函数可以进行对全表扫描做出相应的规范。
3、数据遍历与显示ScannerResult
通过上述两种方法可以发送对一张表是遍历请求,当发送后,服务器会相应的启动全表扫面程序,从而准备向客户端返回相应的数据。因此根据客户端的遍历需要对数据尽心请求,然后将请求的结果进行返回,客户端拿到后进行展示
(1)next()的单行返回数据的方法
ResultScanner rs=table.getScanner(Bytes.toBytes(family));
//进项单行获取演示
Result result=null;
while((result=rs.next())!=null)
{
KeyValue[] kvs=result.raw();
for(KeyValue kv:kvs)
{
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
}
}
rs.close();
next()方法会默认想客户端请求发送一行数据请求,刚服务器端的scan程序接收到请求后会将经需要返回的数据封装成一个result对象返回给客户端,因此客户端可以通过result对象去接受该行数据。接收到的数据则跟Get中的result使用方法是相同的。
(2)next(int n)的多行返回数据的方法
next(int n):该函数会向服务器发送多个请求,以返回多条数据请求。
//一次获取多个结果(行数据)进行展示
rs=table.getScanner(Bytes.toBytes(family));
Result[] results=null;
while((results=rs.next(2))!=null)
{
for(Result r:results)
{
KeyValue[] kvs=r.raw();
for(KeyValue kv:kvs)
{
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
}
}
}
rs.close();
next(int n)函数返回的是一个result数组。用户接受到数据后可以进行相应的操作。
(3)迭代器遍历
//进行迭代的方式进行输出
rs=table.getScanner(Bytes.toBytes(family));
Iterator<Result> resultIterator=rs.iterator();
while(resultIterator.hasNext())
{
result=resultIterator.next();
KeyValue[] kvs=result.raw();
for(KeyValue kv:kvs)
{
System.out.println(Bytes.toString(kv.getRow()));
System.out.println(Bytes.toString(kv.getFamily()));
System.out.println(Bytes.toString(kv.getQualifier()));
System.out.println(Bytes.toString(kv.getValue()));
}
}
//关闭表
rs.close();
(4)注意点
因为当用户发送一个scan全表扫描后,region服务器会为全表扫描创建扫描资源,因此长时间启用全表扫描的话会占用region服务器的大量资源,所以在要求在使用完scanner扫描器后尽快释放掉资源。
rs.close() 会告知服务其扫描器租约已经结束,服务器就会释相应的全局扫描的资源。
三、Scan对象
(1)setStartRow() / setStopRow
设置扫描的开始行与结束行,通过这两个可以直接确scan在扫描的范围,通过缩小范围可以减少扫描到时间,从而提高扫描的效率
(2)addFamily() / addColumn()
通过这两个函数,可以在列或者列簇上的扫描位置。HBase是面向lie出书的数据库,而同一个列簇的数据全部存放在同一个位置文件中。因此如果可以确定扫描那个一列簇时,可以减少扫描的范围,从而缩短扫描的时间。而在确定到某一个列时也会因为HBase的面向列存储使得其效率提高。
(3)setMaxVersion() / setMaxVersion(int version)
设置返回的版本数量,默认为返回最新的数据。第一个函数则会返回所有的版本数据,第二个函数可以设置返回的版本数量
(4)setTimeStamp(long max)
返回该时间戳的数据
(5)setTimeRange(long min,long max)
设定返回的时间戳的范围,只有版本值在该范围之内的数据才会被返回到客户端
(6)setFilter(Filter f)
设置过滤器,有时候扫描全表返回的数量过大时,可以通过过滤器将不符合的数据进行过滤,这样可以减少从服务器到客户端的数据传送,挺高扫描效率。
(7)setCacheBlocks(boole open)
在进行全表扫描过程中,服务器端提供了一个缓存区,该缓存区可以将指定的数据量全部放入到内存中,这样可以提高读取效率。缓存区的打开也可以通过htable客户端进行打开。在开发后用户可以通过 setCache(int n)的方式设置每次缓存的数量为多少。通过调整该函数以提高读取的效率。
四、总结
scan的全表扫描区别于其他三个操作,虽然获取数据与Get获取的数据是相同的,其与Get也具有形似的属性,可以通过修改这些属性去对数据获取进行调优,从而使得提高数据获取的效率。