本文主要来了解下HBase的写入流程,先大致说一下写数据的流程:
- 从Zookeeper中获取meta表的信息,然后从中找到对应的Region
- 找到对应的RegionServer,将数据在WAL日志和MemStore上各写一份
- MemStore达到一定阈值之后会将数据刷写成一个StoreFile
客户端Put是如何进行的:
如果一个Put就往服务端发送一次的话,那么就会有很多个RPC请求,效率就不高了,如果将一批Put放到一起,达到一定量级之后再发送到服务端,这样就可以提高Put的效率了,而客户端也正是有这样的优化。
客户端有一个写缓冲区(write buffer),默认情况下是禁用的,可以通过table.setAutoFlush(false) 禁用自动刷写,从而启动写缓冲区。通过setWriteBufferSize()来设置缓冲区的大小,然后可以显示或者隐式的将缓冲区的数据写到服务端。
Put请求在客户端会进行排序分组,通过向每个Region服务器发送RPC请求将数据写到服务端。如果一批Put请求中间有一些失败了,那么这些失败的Put示例会保存在本地写缓冲区里面,可以调用Htable的getWriteBuffer()来处理它们。
服务端Put是如何进行的:
服务端最终会在RSRpcServices.java类中调用HRegion中的batchMutate(Mutation[] mutations, long nonceGroup, long nonce)方法,对发送到该Region中的数据进行处理。
首先会进行checkResources(),判断Memstore的大小是不是已经达到一定阈值,如果达到了,会首先进行Flush,然后抛出RegionTooBusyException,本次数据的写入就不进行了。
判断大小是否超过blockingMemStoreSize,超过即阻塞当前写:
然后调用doMiniBatchMutation(batchOp)方法,这个方法是Put写入的核心,后续会仔细分析这个方法。
最后会再次检查Memstore的大小,判断是否超过memstoreFlushSize,是的话要进行Flush操作。
看下doMiniBatchMutation(batchOp)方法:
内部主要就是获取读锁,写WAL日志以及Memstore,最后更新MVCC,使得数据可见。
为什么写的时候不去获取写锁,是因为MVCC已经保证数据的一致性了,就没必要加写锁了,在increment/append/batch中由于需要保证原子性需要加写锁。
注意,如果数据没有时间戳,那么会把当前的时间赋值给这条记录:
并且是写WAL日志和Memstore的顺序是:先写WAL,再写Memstore,最后再Sync WAL日志,类似一个二阶段提交操作。
最后修改MVCC,使得数据可见,写入流程就执行完毕了。差不多就是这个逻辑,Flush操作后续有时间单独说下。
参考:
《HBase权威指南》
https://blog.csdn.net/qq_18298439/article/details/88592861(HBase整体介绍)