HBase中得数据结构和组织架构

数据组织

让我们回顾一下 HBase 数据的组织架构,首先 Table 横向切割为多个 HRegion ,按照一个列族的情况,每一个 HRegion 之中包含一个 MemStore 和多个 HFile 文件, HFile 文件设计比较复杂,这里不详细展开,用户需要知道给定一个 rowkey 可以根据索引结合二分查找可以迅速定位到对应的数据块即可。结合这些背景信息,我们可以把一个Read请求的处理转化下面的问题:如何从一个 MemStore,多个 HFile 中获取到用户需要的正确的数据(默认情况下是最新版本,非删除,没有过期的数据。同时用户可能会设定 filter ,指定返回条数等过滤条件)

在 RegionServer 内部,会把读取可能涉及到的所有组件都初始化为对应的 scanner 对象,针对 Region 的读取,封装为一个 RegionScanner 对象,而一个列族对应一个 Store,对应封装为 StoreScanner,在 Store 内部,MemStore 则封装为 MemStoreScanner,每一个 HFile 都会封装为 StoreFileScanner 。最后数据的查询就会落在对 MemStoreScanner 和 StoreFileScanner 上的查询之上。

这些 scanner 首先根据 scan 的 TimeRange 和 Rowkey Range 会过滤掉一些,剩下的 scanner 在 RegionServer 内部组成一个最小堆 KeyValueHeap,该数据结构核心一个 PriorityQueue 优先级队列,队列里按照 Scanner 指向的 KeyValue 排序。

// 用来组织所有的Scanner

protected PriorityQueue heap = null;

 

// PriorityQueue当前排在最前面的Scanner

protected KeyValueScanner current = null;

数据过滤

我们知道数据在内存以及 HDFS 文件中存储着,为了读取这些数据,RegionServer 构造了若干 Scanner 并组成了一个最小堆,那么如何遍历这个堆去过滤数据返回用户想要的值呢。

我们假设 HRegion 有4个 Hfile,1个 MemStore,那么最小堆内有4个 scanner 对象,我们以 scannerA-D 来代替这些 scanner 对象,同时假设我们需要查询的 rowkey 为 rowA。每一个 scanner 内部有一个 current 指针,指向的是当前需要遍历的 KeyValue,所以这时堆顶部的 scanner 对象的 current 指针指向的就是 rowA(rowA:cf:colA)这条数据。通过触发 next() 调用,移动 current 指针,来遍历所有 scanner 中的数据。scanner 组织逻辑视图如下图所示。


 

第一次 next 请求,将会返回 ScannerA中的rowA:cf:colA,而后 ScannerA 的指针移动到下一个 KeyValue rowA:cf:colB,堆中的 Scanners 排序不变;

第二次 next 请求,返回 ScannerA 中的 rowA:cf:colB,ScannerA 的 current 指针移动到下一个 KeyValue rowB:cf:ColA,因为堆按照 KeyValue 排序可知 rowB 小于 rowA, 所以堆内部,scanner 顺序发生改变,改变之后如下图所示:


 

scanner 内部数据完全检索之后会 close 掉,或者 rowA 所有数据检索完毕,则查询下一条。默认情况下返回的数据需要经过 ScanQueryMatcher 过滤返回的数据需要满足下面的条件:

  • keyValue类型为put
  • 列是Scanner指定的列
  • 满足filter过滤条件
  • 最新的版本
  • 未删除的数据

如果 scan 的参数更加复杂,条件也会发生变化,比如指定 scan 返回 Raw 数据的时候,打了删除标记的数据也要被返回,这部分就不再详细展开,至此读流程基本解析完成,当然本文介绍的还是很粗略,有兴趣的同学可以自己研究这一部分源码。

你可能感兴趣的:(大数据)