天天看点

hbase源码系列(二)HTable 如何访问客户端1.Put操作2.Delete操作3.Get操作 4.批量操作

  hbase的源码终于搞一个段落了,在接下来的一个月,着重于把看过的源码提炼一下,对一些有意思的主题进行分享一下。继上一篇讲了负载均衡之后,这一篇我们从client开始讲吧,从client到master再到region

server,按照这个顺序来开展,网友也可以对自己感兴趣的部分给我留言或者直接联系我的qq。

  现在我们讲一下htable吧,为什么讲htable,因为这是我们最常见的一个类,这是我们对hbase中数据的操作的入口。

  

  下面是一个很简单往hbase插入一条记录的例子。

  我们平常就是采用这种方式提交的数据,为了提高重用性采用htablepool,最新的api推荐使用hconnection.gettable("test")来获得htable,旧的htablepool已经被抛弃了。好,我们下面开始看看htable内部是如何实现的吧,首先我们看看它内部有什么属性。

  主要是靠上面的这些家伙来干活的,这里面的connection、ap、rpccallerfactory是用来和后台通信的,htable只是做一个操作,数据进来之后,添加到writeasyncbuffer,满足条件就flush。

  下面看看table.put是怎么执行的:

  执行put操作,如果是autofush,就提交,先看doput的过程,如果之前的ap异步提交到有问题,就先进行后台提交,不过这次是同步的,如果没有错误,就把put添加到队列当中,然后检查一下当前的

buffer的大小,超过我们设置的内容的时候,就flush掉。

  写下来,让我们看看backgroundflushcommits这个方法吧,它的核心就这么一句ap.submit(writeasyncbuffer,

true) ,如果出错了的话,就报错了。所以网上所有关于客户端调优的方法里面无非就这么几种:

1)关闭autoflush

2)关闭wal日志

3)把writebuffersize设大一点,一般说是设置成5mb

  经过实践,就第二条关闭日志的效果比较明显,其它的效果都不明显,因为提交的过程是异步的,所以提交的时候占用的时间并不多,提交到server端后,server还有一个写入的队列,(⊙o⊙)…

让人想起小米手机那恶心的排队了。。。所以大规模写入数据,别指望着用put来解决。。。mapreduce生成hfile,然后用bulk

load的方式比较好。

  不废话了,我们继续追踪ap.submit方法吧,f3进去。

  循环遍历r,为每个r找到它的位置loc,loc是hregionlocation,里面记录着这行记录所在的目标region所在的位置,loc怎么获得呢,走进finddestlocation方法里面,看到了这么一句。

  通过表名和rowkey,使用hconnection就可以定位到它的位置,这里就先不讲定位了,稍后放一节出来讲,否则篇幅太长了,这里我们只需要记住,提交操作,是要知道它对应的region在哪里的。

  定位到它的位置之后,它把loc添加到了actionsbyserver,一个region

server对应一组操作。(插句题外话为什么这里叫action呢,其实我们熟知的put、delete,以及不常用的append、increment都是继承自row的,在接口传递时候,其实都是视为一种操作,到了后台之后,才做区分)。

  接下来,就是多线程的rpc提交了。

  再深挖一点,把它们的实现都扒出来吧。

  ok,看到了,先构造一个multiservercallable,然后再通过rpccallerfactory做最后的call操作。

  好了,到这里再总结一下put操作吧,前面写得有点儿凌乱了。

  (1)把put操作添加到writeasyncbuffer队列里面,符合条件(自动flush或者超过了阀值writebuffersize)就通过asyncprocess异步批量提交。

  (2)在提交之前,我们要根据每个rowkey找到它们归属的region

server,这个定位的过程是通过hconnection的locateregion方法获得的,然后再把这些rowkey按照hregionlocation分组。

  (3)通过多线程,一个hregionlocation构造multiservercallable<row>,然后通过rpccallerfactory.<multiresponse>

newcaller()执行调用,忽略掉失败重新提交和错误处理,客户端的提交操作到此结束。

  对于delete,我们也可以通过以下代码执行一个delete操作

  这个操作比较干脆,new一个regionservercallable<boolean>,直接走rpc了,爽快啊。

 这里面注意一下这行mutateresponse response =

getstub().mutate(null, request);

 getstub()返回的是一个clientservice.blockinginterface接口,实现这个接口的类是hregionserver,这样子我们就知道它在服务端执行了hregionserver里面的mutate方法。

  get操作也和delete一样简单

  get操作也没几行代码,还是直接走的rpc

  注意里面的protobufutil.get操作,它其实是构建了一个getrequest,需要的参数是regionname和get,然后走hregionserver的get方法,返回一个getresponse

  针对put、delete、get都有相应的操作的方式:

  1.put(list)操作,很多童鞋以为这个可以提高写入速度,其实无效。。。为啥?因为你构造了一个list进去,它再遍历一下list,执行doput操作。。。。反而还慢点。

  2.delete和get的批量操作走的都是connection.processbatchcallback(actions,

tablename, pool, results, callback),具体的实现在hconnectionmanager的静态类hconnectionimplementation里面,结果我们惊人的发现:

  它走的还是put一样的操作,既然是一样的,何苦代码写得那么绕呢?

  okay,htable到现在就告一段落了,后续会有在region

server这端关于put、delete、get的处理的文章,敬请期待。