HBase作为分布式NoSQL数据库系统,不单支持宽列表,并且对于随机读写来说也具有较高的性能。在高性能的随机读写事务的同时,HBase也能保持事务的一致性。目前HBase只支持行级别的事务一致性。本文主要探讨一下HBase的写请求流程,主要基于0.98.8版本的实现。
客户端写请求
HBase提供的Java client API是以HTable为主要接口,对应其中的HBase表。写请求API主要为HTable.put(write和update)、HTable.delete等。以HTable.put为例子,首先来看看客户端是怎么把请求发送到HRegionServer的。
每个put请求表示一个KeyValue数据,考虑到客户端有大量的数据需要写入到HBase表,HTable.put默认是会把每个put请求都放到本地缓存中去,当本地缓存大小超过阀值(默认为2MB)的时候,就要请求刷新,即把这些put请求发送到指定的HRegionServer中去,这里是利用线程池并发发送多个put请求到不同的HRegionServer。但如果多个请求都是同一个HRegionServer,甚至是同一个HRegion,则可能造成对服务端造成压力,为了避免发生这种情况,客户端API会对写请求做了并发数限制,主要是针对put请求需要发送到的HRegionServer和HRegion来进行限制,具体实现在AsyncProcess中。主要参数设定为:
- hbase.client.max.total.tasks 客户端最大并发写请求数,默认为100
- hbase.client.max.perserver.tasks 客户端每个HRegionServer的最大并发写请求数,默认为2
- hbase.client.max.perregion.tasks 客户端每个HRegion最大并发写请求数,默认为1
为了提高I/O效率,AsyncProcess会合并同一个HRegion对应的put请求,然后再一次把这些相同HRegion的put请求发送到指定HRegionServer上去。另外AsyncProcess也提供了各种同步的方法,如waitUntilDone等,方便某些场景下必须对请求进行同步处理。每个put和读请求一样,都是要通过访问hbase:meta表来查找指定的HRegionServer和HRegion,这个流程和读请求一致,可以参考文章的描述。
服务端写请求
当客户端把写请求发送到服务端时,服务端就要开始执行写请求操作。HRegionServer把写请求转发到指定的HRegion执行,HRegion每次操作都是以批量写请求为单位进行处理的。主要流程实现在HRegion.doMiniBatchMutation,大致如下:
- 获取写请求里指定行的行锁。由于这些批量写请求之间是不保证一致性(只保证行一致性),因此每次只会尝试阻塞获取至少一个写请求的行锁,其它已被获取的行锁则跳过这次更新,等待下次迭代的继续尝试获取
- 更新已经获得行锁的写请求的时间戳为当前时间
- 获取HRegion的updatesLock的读锁。
- 获取MVCC(Mu