文章目录
扫描操作不会通过一次RPC请求返回所有匹配的行,而是以行为单位进行返回。很明显,行的数目很大,可能有上千条甚至更多,同时在一次请求中发送大量数据,会占用大量的系统资源并消耗很长时间。
ResultScanner类把扫描操作转换为类似的get操作,它将每一行数据封装成一个Result实例,并将所有的Result实例放入一个迭代器中。ResultScanner的一些方法如下:
Result next() throws IOException Result[] next(int nbRows) throws IOException void close()
|
有两种类型next()调用供用户选择。调用close()方法会释放所有由扫描控制的资源。
扫描器租约
要确保尽早释放扫描器实例,一个打开的扫描器会占用不少的服务端资源,累积多了会占用大量的堆空间。当使用完ResultScanner之后调用它的close()方法,同时,当把close()方法放到try/finally块中,以保证其在迭代获取数据过程中出现异常和错误时,仍然能执行close()。
就像行锁一样,扫描器也使用同样的租约超时机制,保护其不被失效的客户单阻塞太久。用户可以使用修改锁租约处提到的那个配置属性来修改超时时间(单位:毫秒):
<property> <name>hbase.regionserver.lease.period</name> <value>120000</value> </property>
|
用户需要确保该属性值适当,这个值要同时适用于锁租约和扫描器租约。
next()调用返回了一个单独的Result实例,这个实例代表了下一个可用的行。此外,用户可以使用next(int nbRows) 一次获取多行数据,它返回一个数组,数组中包含的Result实例最多可达nbRows个,每个实例代表唯一的一行。当用户扫描到表尾或到终止行时,由于没有足够的行来填充数据,返回的结果数组可能会小于既定长度。
下边是实现扫描器获取表中数据代码
import java.io.IOException;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.HBaseConfiguration; import org.apache.hadoop.hbase.client.HTable; import org.apache.hadoop.hbase.client.Result; import org.apache.hadoop.hbase.client.ResultScanner; import org.apache.hadoop.hbase.client.Scan; import org.apache.hadoop.hbase.util.Bytes;
public class HBaseResultScanner { public static void main(String[] args) throws IOException { Configuration conf = HBaseConfiguration.create(); HTable table = new HTable(conf, "testtable"); Scan scan1 = new Scan(); ResultScanner scanner1 = table.getScanner(scan1); for (Result res : scanner1) { System.out.println(res); } scanner1.close(); Scan scan2 = new Scan(); scan2.addFamily(Bytes.toBytes("colfam1")); ResultScanner scanner2 = table.getScanner(scan2); for (Result res : scanner2) { System.out.println(res); } scanner2.close(); Scan scan3 = new Scan(); scan3.addColumn(Bytes.toBytes("colfam1"), Bytes.toBytes("col-5")) .addColumn(Bytes.toBytes("colfam2"), Bytes.toBytes("col-33")) .setStartRow(Bytes.toBytes("row-10")) .setStopRow(Bytes.toBytes("row-20")); ResultScanner scanner3 = table.getScanner(scan3); for (Result res : scanner3) { System.out.println(res); } scanner3.close();
}
}
|
代码插入了100行数据,每行有两个列族,每个列族下包含100个列。第一个扫描全表内容,第二个扫描操作只扫描一个列族,最后一个操作有严格的限制条件,其中包括对行范围的限制,同时还要求只扫描两个特定的列。
输出如下:
Scanning table # 3... keyvalues={row-10/colfam1:col-5/1300803775078/Put/vlen=8,row-10/colfam2:col-33/1300803775099/Put/vlen=9} keyvalues={row-100/colfam1:col-5/1300803775079/Put/vlen=9, row-100/colfam1:col-33/1300803775095/Put/vlen=10 } keyvalues={row-11/colfam1:col-5/1300803775152/Put/vlen=8, row-11/colfam1:col-33/1300803775170/Put/vlen=9 } keyvalues={row-12/colfam1:col-5/1300803775212/Put/vlen=8, row-12/colfam1:col-33/1300803775246/Put/vlen=9 } keyvalues={row-13/colfam1:col-5/1300803775345/Put/vlen=8, row-13/colfam1:col-33/1300803775376/Put/vlen=9 } keyvalues={row-14/colfam1:col-5/1300803775479/Put/vlen=8, row-14/colfam1:col-33/1300803775498/Put/vlen=9 } keyvalues={row-15/colfam1:col-5/1300803775554/Put/vlen=8, row-15/colfam1:col-33/1300803775582/Put/vlen=9 } keyvalues={row-16/colfam1:col-5/1300803775665/Put/vlen=8, row-16/colfam1:col-33/1300803775687/Put/vlen=9 } keyvalues={row-17/colfam1:col-5/1300803775734/Put/vlen=8, row-17/colfam1:col-33/1300803775748/Put/vlen=9 } keyvalues={row-18/colfam1:col-5/1300803775791/Put/vlen=8, row-18/colfam1:col-33/1300803775805/Put/vlen=9 } keyvalues={row-19/colfam1:col-5/1300803775843/Put/vlen=8, row-19/colfam1:col-33/1300803775859/Put/vlen=9 } keyvalues={row-2/colfam1:col-5/1300803774463/Put/vlen=7, row-2/colfam1:col-33/1300803774485/Put/vlen=8 }
|
再强调一次,匹配的行键都是按词典序列排列的,这使得结果非常有趣。用户可以简单地用0把行键补齐,这样扫描出来的结果顺序更有可读性。