HTablePool的使用和源码分析

HTable与HTablePool

HTable是HBase的客户端API,用来实现对hbase表的增加(Create)、查询(Retrieve)(重新得到数据)、更新(Update)和删除(Delete),简称CRUD操作。但是HTable并不是线程安全的。在HTable的API说明中写道:

HTable适合对单表操作,对读或写操作都不是线程安全的。对于写操作(Put或Delete),如果多线程共享一个HTable实例,写缓冲区可能会被破坏。对于读操作,一些被scan使用的字段同时被多个线程共享,如果此时有Get操作,不能保证是安全的。

创建HTable的开销较大,多个HTable实例可共享一个Configuration。

文档中建议使用HTablePool。它就是个对象池,可以复用HTable实例,使用的时候通过getTable方法获取一个HTable对象,然后可以进行各种 scan/get/put/delete 等操作,使用完调用close()方法将htable对象归还到池中。

HTablePool的使用

一个典型的使用HTablePool的代码例子:

import java.io.IOException;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HTableInterface;
import org.apache.hadoop.hbase.client.HTablePool;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;

public class HTablePoolTest 
{
    private static int SIZE = 15;

    public static void main(String[] agrs) throws IOException {
        Configuration conf = HBaseConfiguration.create();
        HTablePool pool = new HTablePool(conf, SIZE);
        HTableInterface htable = pool.getTable("baoniu1");

        // do something
        String rowkey = "06:123456";
        Get get = new Get(Bytes.toBytes(rowkey));
        get.addFamily(Bytes.toBytes("a"));
        try {
            Result res = htable.get(get);
            KeyValue[] kv = res.raw();
            for (int i = 0; i < kv.length; i++) {
                System.out.print(new String(kv[i].getRow()) + " ");
                System.out.print("cf:qualify=" + new String(kv[i].getFamily()) + ":");
                System.out.print(new String(kv[i].getQualifier()) + " ");
                System.out.println("v=" + new String(kv[i].getValue()));
            }
        } catch (IOException e) {
            throw new RuntimeException("Exception of get rowkey=" + rowkey, e);
        } finally {
            htable.close(); // return to pool
        }
    }
}

HTablePool分析

maxSize

在HTablePool中,针对每个表,都用PoolMap<string, htableinterface=””> tables对象存储了一个表名到pool的映射。maxSize指的就是这个pool的大小,表示每一个表的最大引用个数。在HTablePool中的多个HTable实例会共享同一个Configuration。 当调用getTable方法时,在findOrCreateTable方法中会判断当前是否有可用的htable,否则就通过HTableInterfaceFactory创建一个。

public HTableInterface getTable(String tableName) {
    // call the old getTable implementation renamed to findOrCreateTable
    HTableInterface table = findOrCreateTable(tableName);
    // return a proxy table so when user closes the proxy, the actual table
    // will be returned to the pool
    return new PooledHTable(table);
}

返回的hable会被包装成一个PooledHTable,PooledHTable是htable的代理,里面唯一不同的是close()方法里调用了returnTable(table)来将htabe归还到池中。

private void returnTable(HTableInterface table) throws IOException {
    // this is the old putTable method renamed and made private
    String tableName = Bytes.toString(table.getTableName());
    if (tables.size(tableName) &gt;= maxSize) {
      // release table instance since we're not reusing it
      this.tables.remove(tableName, table);
      this.tableFactory.releaseHTableInterface(table);
      return;
    }
    tables.put(tableName, table);
}

如果tables.size>=maxsize,会移除掉这个HTable实例,而releaseHTableInterface调用的就是HTable的close方法,close方法会调用flushCommits(),强制flush write buffer。

这是一个需要注意的地方:hbase中有个autofulsh的参数,表示是否对client端请求进行缓存。

如果autoflush=false,当客户端提交delete或put请求时,该请求会在客户端缓存,直到数据超过2M(由hbase.client.write.buffer决定)或用户执行了hbase.flushcommits()时才向regionserver提交请求。因此即使htable.put()执行返回成功,也并非说明请求真的成功了。假如还没有达到该缓存而client崩溃,该部分数据将由于未发送到regionserver而丢失。这对于零容忍的在线服务是不可接受的。

所以hbase默认的配置是autoflush=true,表示每次请求都会发往regionserver, 而regionserver接收到请求后第一件事就是写hlog,这样能保证数据不会丢,但是对磁盘IO要求比较高,写入速度会较低。

使用HTablePool的htable对象,调用close()方法会触发flushCommits(),如果有某些应用将autoflush=false想提升写入速度会失效。

PoolType

HTablePool提供了三种类型的pool:Reusable, ThreadLocal, RoundRobin,默认的是reusable。

  1. ReusablePool内部是用ConcurrentLinkedQueue来保存htable实例,保证了线程安全,主要用于多线程复用。如果调用get()方法,内部调的是queue的poll(),会从当前队列头部取出一个htable实例或者返回null。
  2. ThreadLocalPool用ThreadLocalPool来实现pool和使用它的线程进行绑定,因此pool的大小和线程个数相同。
  3. RoundRobinPool内部是用CopyOnWriteArrayList来保存htable实例。它是为了平衡对htable资源的使用,每次都会返回不同的htable实例。但是在HTablePool的构造函数中,不能创建这种类型的pool。

HTableInterfaceFactory

此外,HTablePool还支持通过HTableFactory来实现自定义的HTable。

发表评论

电子邮件地址不会被公开。 必填项已用*标注