HBase为了提供实时查询,以及较高性能的写请求事务吞吐量,对读/写请求都做了大量的优化。经过阅读源码和远程调试,大致了解了读请求的流程。本文主要是基于HBase的0.98.8版本的实现。
客户端读请求
HBase为客户端提供了的读请求API主要有两个,get和scan。其中,get是通过指定单个的rowKey,获取其对应的value值。而scan是指定startRow和stopRow两个边界rowKey来指定范围rowKey值,获取它们对应的value值。
首先我们知道HBase会根据算法把一个表划分为多个Region,然后HMaster会把这几个Region分配到不同的节点中去,因此,当我们查找一个表的数据时,有可能要把请求发送到不同的节点,再获取其查询结果。但是要如何根据get指定的rowKey到指定的节点上查询呢(scan的流程相差不大)?HBase依靠hbase:meta这个系统表实现,事实上hbase:meta表和普通的HBase表一样,都是要分配到某个节点上,如果这个节点出现故障就会转移到另外可用的节点上,这样就可以防止某个节点出现故障导致整个HBase服务不可用。
HBase要求客户端的读请求需要自行定位到所属的Region的节点上,然后把请求的rowKey以及Region一并发送到这个节点。
我们来看看客户端的读请求执行过程:
由于hbase:meta表在存放在某台可用的节点机器上,因此我们需要定位究竟是哪台节点。这个过程步骤如下:
- HBase使用zookeeper来存放hbase:meta表的位置,这样客户端就需要先到zookeeper查询hbase:meta表在哪个节点上,如图中的1。
- 然后在根据读请求的rowKey发送请求到这个节点上,节点根据hbase:meta表查找到rowKey所属Region的节点,然后返回给客户端,即图中的2,3。
- 客户端再真正把读请求发送到指定的HRegionServer上,如图中的4,然后HRegionServer再执行具体读请求的查询操作,最后把结果返回到客户端上
要注意的是,客户端会缓存每次hbase:meta表返回的结果缓存到本地上,这样就可以防止每次查询都要经过步骤1,2进行查询,导致过多的网络I/O操作。在上面的第2个过程中,节点是如何根据rowKey通过查询hbase:meta表得到所属的Region呢?hbase:meta表中保存了每个Region的startRow,这样可以查找小于rowKey里的最大startRow值即可确定Region,这个过程在HRegion.getClosestRowBefore()代码实现。
对于scan来说,其实流程和get的查询相差不大,由于scan请求包含startRow和stopRow两个范围,因此可能需要查询多个Region。具体步骤如下:
- 会先发送startRow作为指定rowKey到hbase:meta表上请求Region信息,返回的Region信息会包含这个Region的startKey和endKey
- 然后客户端再根据Regioin的范围判断。即如果stopRow<=endKey,则只需要查询这个Region即可。
- 否则,需要首先把startRow和endKey作为一次的请求发送到这个Region的节点上,然后再把endKey作为新的startRow去查询hbase:meta,然后重复步骤1
这样经过多次的迭代,即可把scan请求对应为多个Region的查询。