在单线程环境下使用hbase的htable是没有问题,但是突然高并发多线程情况下就可能出现问题。
以下为Htable的API说明:
代码语言:javascript复制This class is not thread safe for updates; the underlying write buffer can be corrupted if multiple threads contend over a single HTable instance.
当有多个线程竞争时可能把当前正在写的线程corrupted,那么原因是什么呢?
根据Htable的源码:
代码语言:javascript复制public HTable(final byte [] tableName)
throws IOException {
this(HBaseConfiguration.create(), tableName);
}
public static Configuration create() {
Configuration conf = new Configuration();
return addHbaseResources(conf);
}
从上面我们可以看到每一个HTable的实例化过程都要创建一个新的conf,我们甚至可以认为一个conf对应的是一个HTable的connection,因此如果客户端对于同一个表,每次新new 一个configuration对象的话,那么意味着这两个HTable虽然操作的是同一个table,但是建立的是两条链接connection,它们的socket不是共用的,在多线程的情况下,经常会有new Htable的情况发生,而每一次的new都可能是一个新的connection,而我们知道zk上的链接是有限制的如果链接达到一定阈值的话,那么新建立的链接很有可能挤掉原先的connection,而导致线程不安全。
因此hbase官方文档建议我们:HTable不是线程安全的。建议使用同一个HBaseConfiguration实例来创建HTable实例,这样可以共享ZooKeeper和socket实例
。例如,最好这样做:
HBaseConfiguration conf = HBaseConfiguration.create();
HTable table1 = new HTable(conf, "myTable");
HTable table2 = new HTable(conf, "myTable");
而不是这样:
代码语言:javascript复制HBaseConfiguration conf1 = HBaseConfiguration.create();
HTable table1 = new HTable(conf1, "myTable");
HBaseConfiguration conf2 = HBaseConfiguration.create();
HTable table2 = new HTable(conf2, "myTable");
当然最方便的方法就是使用HTablepool了,维持一个线程安全的map里面存放的是tablename和其引用的映射,可以认为是一个简单的计数器,当需要new 一个HTable实例时直接从该pool中取,用完放回。