最近在看HBase,琢磨了一下HBase的写入流程,我的测试环境安装的HBase版本是2.1.0-cdh6.3.2,那么直接在IDEA配置好POM文件,下载对应版本的Jar包和文档,找到org.apache.hadoop.hbase.regionserver.HRegion.java,开撕源码。
可以找到源码中关于这部分的注释比较清晰明了:

STEP 1. Try to acquire as many locks as we can and build mini-batch of operations with
locked rows

STEP 2. Update mini batch of all operations in progress with LATEST_TIMESTAMP timestamp
We should record the timestamp only after we have acquired the rowLock,
otherwise, newer puts/deletes are not guaranteed to have a newer timestamp

STEP 3. Build WAL edit

STEP 4. Append the WALEdits to WAL and SYNC.

STEP 5. Write back to memStore

STEP 6. Complete MiniBatchOperations: If required calls postBatchMutate() CP hook and
complete mvcc for last writeEntry

STEP 7. Apply to memstore

首先HBase首先获取一些锁,在导入的包中,可以发现import java.util.concurrent.locks.Lock;
不难看出这里的获取的锁是JUC的锁。

然后构建mini-batch,并使用最后的时间戳来更新mini-batch;

把操作追加到WAL并且同步

再写入menStore,写入成功以后才在Finally中释放锁。

Hbase2.1.0版本与1.3.1版本相比,有了不少的改进:

  1. 加入了mini-batch,每次更新是针对小批量的操作,提高了整体的吞吐率;
  2. WAL的同步时机不同,在1.3.1版本中是先写入WALedits,再写入memStore,最后才对WAL进行同步,在2.1.0版本中的顺序变成了先写WAL并同步,然后再写memStore。在2.1.0的顺序更加符合我们的逻辑,理解起来也更简单。

总结一下HBase的写入逻辑:

  1. Client先访问zookeeper,获取hbase:meta表位于哪个Region Server。
  2. 访问对应的Region Server,获取hbase:meta表,根据读请求的namespace:table/rowkey,查询出目标数据位于哪个Region Server中的哪个Region中。并将该table的region信息以及meta表的位置信息缓存在客户端的meta cache,方便下次访问。
  3. 与目标Region Server进行通讯;
  4. 将数据顺序追加到WAL;
  5. 将数据写入对应的MemStore,数据会在MemStore进行排序;
  6. 向客户端发送ack;
  7. 等达到MemStore的刷写时机后,将数据刷写到HFile。

看过源码之后,对于HBase的写入有了更深入的认识。