HBase(三)java操作HBase

一、客户端正确使用概述

在使用过程中经常会遇见有用户咨询诸如“HBase是否支持连接池?”这样的问题,也有用户因为应用中创建的Connection对象过多,触发了zookeeper的连接数限制,导致客户端连不上的。究其原因,都是因为对HBase客户端的原理不了解造成的。本文简单介绍HBase客户端的Connection对象与socket连接的关系,并给出Connection的正确用法。

Connection是什么
在用户使用HBase过程中,常见的使用Connection的错误方法有:

(1)自己实现一个Connection对象的资源池,每次使用都从资源池中取出一个Connection对象。

(2)每个线程一个Connection对象。

(3)每次访问HBase的时候临时创建一个Connection对象,使用完之后调用close关闭连接。

从这些做法来看,这些用户显然是把Connection对象当成了单机数据库里面的连接对象来用了。然而,作为一个分布式数据库,HBase客户端需要和多个服务器中的不同服务角色建立连接,所以HBase客户端中的Connection对象并不是简单对应一个socket连接。HBase的API文档当中对Connection的定义是:A cluster connection encapsulating lower level individual connections to actual servers and a connection to zookeeper.

我们知道,HBase访问一条数据的过程中,需要连接三个不同的服务角色:

(1)Zookeeper

(2)HBase Master

(3)HBase RegionServer 而HBase客户端的Connection包含了对以上三种socket连接的封装。

在HBase客户端代码中,真正对应socket连接的是RpcConnection对象。HBase使用PoolMap这种数据结构来存储客户端到HBase服务器之间的连接。PoolMap封装了ConcurrentHashMap>的结构,key是ConnectionId(封装了服务器地址和用户ticket),value是一个RpcConnection对象的资源池。当HBase需要连接一个服务器时,首先会根据ConnectionId找到对应的连接池,然后从连接池中取出一个连接对象。

HBase中提供了三种资源池的实现,分别是Reusable,RoundRobin和ThreadLocal。具体实现可以通过hbase.client.ipc.pool.type配置项指定,默认为Reusable。连接池的大小也可以通过hbase.client.ipc.pool.size配置项指定,默认为1。
HBase客户端默认的是连接池大小是1,也就是每个RegionServer 1个连接。如果应用需要使用更大的连接池或指定其他的资源池类型,也可以通过修改配置实现:

config.set("hbase.client.ipc.pool.type",...);
config.set("hbase.client.ipc.pool.size",...);
connection = ConnectionFactory.createConnection(config);

连接HBase的正确姿势
从以上分析不难得出,在HBase中Connection类已经实现了对连接的管理功能,所以我们不需要自己在Connection之上再做额外的管理。另外,Connection是线程安全的,而Table和Admin则不是线程安全的,\
因此正确的做法是一个进程共用一个Connection对象,而在不同的线程中使用单独的Table和Admin对象。

///所有进程共用一个connection对象
connection = ConnectionFactory.createConnection(config);
...
///每个线程使用单独的table对象
Table table = connection.getTable(TableName.valueOf("test"));           
try {
   ...
} finally {
   table.close();
}

二、HBase客户端Write Buffer 介绍及设置

本小节摘自:HBase客户端Write Buffer 介绍及设置
默认情况下,一次Put操作即要与Region Server执行一次RPC操作,其执行过程可以被拆分为以下三个部分:

T1:RTT(Round-Trip Time),即网络往返时延,它指从客户端发送数据开始,到客户端收到来自服务端的确认,总共经历的时延,不包括数据传输的时间;
T2:数据传输时间,即Put所操作的数据在客户端与服务端之间传输所消耗的时间开销,当数据量大的时候,T2的时间开销不容忽略;
T3:服务端处理时间,对于Put操作,即写入WAL日志(如果设置了WAL标识为true)、更新MemStore等。
其中,T2和T3都是不可避免的时间开销,那么能不能减少T1呢?假设我们将多次Put操作打包起来一次性提交到服务端,则可以将T1部分的总时间从T1 * N降低为T1,其中T1指的是单次RTT时间,N为Put的记录条数。

正是出于上述考虑,HBase为用户提供了客户端缓存批量提交的方式(即Write Buffer)。假设RTT的时间较长,如1ms,则该种方式能够显著提高整个集群的写入性能。

那么,什么场景下适用于该种模式呢?下面简单分析一下:
如果Put提交的是小数据(如KB级别甚至更小)记录,那么T2很小,因此,通过该种模式减少T1的开销,能够明显提高写入性能。
如果Put提交的是大数据(如MB级别)记录,那么T2可能已经远大于T1,此时T1与T2相比可以被忽略,因此,使用该种模式并不能得到很好的性能提升,不建议通过增大Write Buffer大小来使用该种模式。

弊端:首先,Write Buffer存在于客户端的本地内存中,那么当客户端运行出现问题时,会导致在Write Buffer中未提交的数据丢失;由于HBase服务端还未收到这些数据,因此也无法通过WAL日志等方式进行数据恢复。
其次,Write Buffer方式本身会占用客户端和HBase服务端的内存开销

发布了52 篇原创文章 · 获赞 7 · 访问量 3797

猜你喜欢

转载自blog.csdn.net/maomaoqiukqq/article/details/103377707