1.设置Scan缓存
1.scan中的setCaching与setBatch方法的区别是什么呢?
setCaching设置的值为每次rpc的请求记录数,默认是1;cache大可以优化性能,但是太大了会花费很长的时间进行一次传输。
setBatch设置每次取的column size;有些row特别大,所以需要分开传给client,就是一次传一个row的几个column。
batch和caching和hbase table column size共同决意了rpc的次数。
setCaching源码-
HTable table = new HTable(config, Bytes.toBytes(tableName));
Scan scanner = new Scan();
/* batch and caching*/
scanner.setBatch(0);
scanner.setCaching(10000);
ResultScnner rsScanner = table.getScanner(scanner);
for(Result res:rsScanner) {
final List<KeyValue> list = res.list();
String rk = null;
StringBuilder sb = new StringBuilder();
for( final KeyValue kv:list) {
sb.append(Bytes.toStringBinary(kv.getValue)) + ",");
rk = getRealRowkey(kv);
}
if(sb.toString().length()>0) {
sb.setLength(sb.toString().length() - 1);
}
System.out.println(rk + "\t" + sb.toString());
}
rsScanner.close();
2.显式地指定列
因为服务器端处理完的结果,需要通过网络传输到客户端,此时,传输的数据量成为瓶颈,如果能有效地过滤部分数据,使用更精确的需求,能够很大程度上减少网络I/O的花费,否则会造成很大的资源浪费。查询过程中,指定某列,或几列,能够有效减少网络传输量
HTable table = new HTable(config, Bytes.toBytes(tableName));
Scan scanner = new Scan();
// 指定列族
scanner.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(column));
/* batch and caching*/
//scanner.setBatch(0);
//scanner.setCaching(10000);
ResultScnner rsScanner = table.getScanner(scanner);
for(Result res:rsScanner) {
final List<KeyValue> list = res.list();
String rk = null;
StringBuilder sb = new StringBuilder();
for( final KeyValue kv:list) {
sb.append(Bytes.toStringBinary(kv.getValue)) + ",");
rk = getRealRowkey(kv);
}
if(sb.toString().length()>0) {
sb.setLength(sb.toString().length() - 1);
}
System.out.println(rk + "\t" + sb.toString());
}
rsScanner.close();
3.关闭ResultScanner
ResultScanner类用于存储服务端扫描的最终结果,可以通过遍历该类获取查询结果。但是,如果不关闭该类,可能出出现服务端在一段时间内一直保持连接,资源无法释放,从而导致服务器端某些资源的不可用,还有可能引发RegionServer的其他问题。
在 上例代码中最后一行
4.禁用块缓存
Scan扫描中可以使用块缓存,通过setCacheBlocks() 方法控制。
如果批量进行全表扫描,例如MapReduce任务读取全表数据,需要将该值设置为false。默认是true。因为全表扫描,每条记录只读取一遍,如果使用块缓存,会降低扫描的效率,但是,对于经常读到的行,建议使用默认值,即使用块缓存。
5.优化行健查询
对表进行全表Scan的时候,如果仅需要行健,而不需要列族,列名,值和时间戳等消息,可以使用过滤器来减少服务器端返回的数据量,降低网络I/O。
使用方法是:使用Scan的setFilter()方法,添加MUST_PASS_ALL操作参数,使用FilterList封装两个过滤器。一个是FirstKeyOnlyFilter,另一个是KeyOnlyFilter。通过这样的过滤器组合,就算在最坏的情况下,RegionServer只会从磁盘读取一个值,同时最小化客户端的网络带宽占用。
6.通过HTableTool访问
HTable对象对于客户端读取数据来说不是线程安全的,因此多线程时,要为每个线程单独创建复用一个HTable对象,不同对象间不要共享HTable对象使用,特别是在客户端AutoFlush被设置为false时,由于存在本地WriteBuffer,可能导致数据不一致。
HTablePool连接池机制可以解决HTable存在的线程不安全问题,同时通过维护固定数量的HTable对象,能够在程序运行期间复用这些HTable资源对象,以节省资源创建时间和降低内存使用空间。HTablePool的使用方法如下:
Public class HTablePoolTest{
protected static String TEST_TABLE_NAME="testtable";
protected static String ROW1_STR="r1";
protected static String COLFAM1_STR="cf1";
protected static String QUAL1_STR="testtable";
private final static byte[] ROW1 = Bytes.toBytes(ROW1_STR);
private final static byte[] COLFAM1=Bytes.toBytes(COLFAM1_STR);
private final static byte[] QUAL1 = Bytes.toBytes(QUAL1_STR);
private static HTablePool pool;
public static init() throws IOException {
Configuration conf = HBaseConfiguration.create();
pool = new HTablePool(conf,10);
// 填充线程池
HTableInterface[] tables = new HTableInterface[10];
for(int i = 0; i < 10; n++) {
tables[n] = pool.getTable(TEST_TABLE_NAME);
}
for(HTableInterface table:tables) {
table.close();
}
}
private Result get() {
HTableInterface table = pool.getTable(TEST_TABLE_NAME);
Get get = new Get(ROW1);
try{
Result result = table.get(get);
return result;
} catch(IOException e) {
} finally {
try {
table.close();
}
}
}
}