日期:2014-05-16  浏览次数:20474 次

Hbase源码分析3-从Put到HFile

Hbase插入数据的过程大致是:

  1. 客户端提交请求给region server(这中间会有作一些缓存)
  2. region server接收到请求,判断其实put请求,将其put到memstore
  3. 每次memstore的操作,都会检查memstore是否操作一个阈值,如果超过,就开始执行flush(),这个flush其实就是从内存中的KeyValue对持久化到HStore(也就是HFile)上面

好了,下面看一下一条数据是怎么从client端到达server端,并且最终转换成HFile的吧~

Client端(HTable):

  1. 执行Put方法
  2. Put方法进一步调用doPut(put)方法
  3. 在doPut里面,会验证一个put的合理性(比如是否指定了column);然后会检查KeyValue的大小是否越界,这个可以通过配置参数hbase.client.keyvalue.maxsize来配置,默认是无限大的~
  4. 然后doPut方法调用writeBuffer.add(put),将这个put写入到本地缓存,只有在以下几种情况下,这个缓存才会flush
    • 超过了writeBufferSize,这个默认是从配置里面加载的,如果没有配置,就为2097152;也可以通过调用HTable的方法setWriteBufferSize()来改变
    • 设置了autoflush,默认autoflush是打开的
    • 手动调用flushCommits()方法
    • 调用close()方法
  5. 在flush方法中,通过执行connection.processBatchOfPuts(),连接远程region server,提交请求:

注:cilent在执行插入的时候,会对最近使用的region server做cache,如果有在cache有保存着相应的region server信息,就直接取出这个region信息,连接这个region server。否则才对master执行一次RPC,获得region server信息
客户端的操作,put,delete,get都是封装在一个对象Action里面的每次提交,都是一系列的Action一起提交,也就是MultiAction。

Server端(HRegionServer):

  1. 执行HRegionServer.multi(MultiActionmulti),处理插入请求。
    • 这里面会取出每一个action对象,判断其属于哪种实例( instanceof Delete/Put/Get),来执行特定的操作。
    • 给每一个put分配一个lock
    • 执行HRegion.put(),正式进行put。
  2. 在HRegion.put()方法中,又会一次调用以下方法
    • checkReadOnly() //检查region是否只读,如果只读,就会抛出异常
    • checkResources()
    • startRegionOperation() //获得锁
    • doMiniBatchPut(batchOp)
  3. doMiniBatchPut(batchOp)方法会进行以下操作:
    • 获得锁
    • 写时间戳
    • 写HLog
    • 写Memstore:执行方法applyFamilyMapToMemstore,这里面会调用Store store = getStore(family);获得store实例,然后调用store的add方法,添加每一个kv对到store里面。

注:在HRegion的put方法中,执行完doMiniBatchPut之后,会检查memstore 的大小是否超过阈值,如果超过,就执行一次flush,flush的时候,会对memstore进行一个快照,就是暂时停止memstore服务,然后立即生成一个新的memstore对象,代替当前memstore接替当前memstore的工作,被替换的memstore会被写到HStore(HFile)中。
Memstore的flush是在方法HRegion.internalFlushcache()里执行的。
切换memstore是非常快的,生成一个新对象,同时把memstore的指向直接切换到新的kvset。