简介:HBase 的存储引擎是基于 LSM-Like 树实现的,更新操作不会间接去更新数据,而是应用各种 type 字段(put,delete)来标记一个新的多版本数据,采纳定期 compaction 的模式来归档合并数据。这种数据结构将写操作变得非常简单且高效,然而却给读造成了很大的困扰。读取过程须要依据列族读取不同 HFile 中的数据;还须要依据版本进行过滤,同时对曾经标记删除的数据也要进行过滤;硬盘中的数据与 MemStore 中的数据重合时,还须要执行合并,最初在内存中拼接成一行残缺的数据再向上返回。本文粗粒度地展现了 HBase 的读取链路,欢送一起探讨交换~
注释之前
在讲 HBase 的读门路时,咱们先来看几个简略的类图。
InternalScanner 是一个 Interface 次要提供了两个办法,next(List<Cell> result)办法——获取下一行的数据。而 next(List<Cell> result, ScannerContext scannerContext)提供性能雷同,只不过容许传入一个 ScannerContext 用以记录以后 scan 工作的上下文,判断是否能够提前结束、是否要去读下一列、是否要去读下一行等。并且产生在 InternalScanner 中的数据比拟等操作,都是基于 byte[](而不必先转化为 RowResults),更加靠近于数据在物理上的存储模式,能够取得更高的性能。
KeyValueScanner 也是一个接口,换成 CellScanner 可能更容易了解。对,它次要提供在一个“可读取的对象上”,获取 cell 的能力。这里应用“可读取的对象”这个词,次要是因为它能够是一个物理概念上的 HFile,但也能够是逻辑意义上有迭代读取能力的 scanner。
最初一个要害的类就是 KeyValueHeap,该类实现了 KeyValueScanner 与 InternalScanner 接口,具备了获取 cell 及获取行的能力。KeyValueHeap 中还有一个要害的属性,为 heap,它是一个 PriorityQueue<KeyValueScanner> 对象,comparator = CellComparatorImp(即依照 key 的格局:rowkeyqualifier:timestamp)。即 KeyValueHeap 容许传入多个 KeyValueScanner,通过 PriorityQueue 的模式将这些 scanner 治理起来,向上提供获取 cell 及获取行数据的能力!
有了 InternalScanner,KeyValueScanner 和 KeyValueHeap 其实曾经能够做很多事件了。
咱们晓得,HBase 的查问抽象地来看的话,是体现为上面这个流程的:
即从不同的 HFile 中进行数据读取,在内存中进行一个 MergeSort,拼接成一行数据向上返回。
你们看 KeyValueScanner、InternalScanner 是不是就像其负责中 HFile 的读取 Scanner,而 KeyValueHeap 负责的其实就是图中的 MergeSort 的工作。KeyValueHeap 管制着上层 KeyValueScanner、InternalScanner 的数据读取,KeyValueScanner、InternalScanner 是真正读取数据的 Scanner。
好,大体的流程思路曾经讲清楚了。其实 HBase 的读取流程远比这简单,波及的对象也更多,但有了下面的根底置信能够了解得很容易,接下来咱们来认真看看 HBase 的读取流程。
注释
咱们从 RegionScanner 登程,认真看看 HBase 的读取流程。
上图中的 RegionScanner 次要靠成员变量 storeHeap,joinedHeap(KeyValueHeap)进行数据读取迭代。而 StoreScanner 也不是一个单纯的 Scanner,而是表演了跟 RegionScanner 相似的角色,它也领有本人的 heap,以此来进行数据的读取。跟【注释之前】说的一样,KeyValueHeap 管制着上层 KeyValueScanner、InternalScanner 的数据读取,KeyValueScanner、InternalScanner 是真正读取数据的 Scanner。只不过 RegionScanner 中多嵌了一层 StoreScanner(KeyValueHeap),变成了这样的调用链路:KeyValueHeap(RegionScanner)->KeyValueHeap(StoreScanner)
->KeyValueScanner,InternalScanner(StoreFileScanner 及 SegmentScanner)。
为什么 HBase 要这样封装?
其实是为了形象不同的性能。
简略来说,
1)StoreScanner 是为了联结 StoreFileScanner 与 SegmentScanner 向上提供整行的数据迭代读取性能。
2)而 RegionScanner,一方面是对获取的数据做了过滤性能,另一方面是为了将全副数据分为两段获取模式(storeHeap 和 joinedHeap),用以优化性能。因为从 storeHeap 中获取的数据如果会被过滤,那么就没有必要再获取 joinedHeap 中的数据了。
具体内容咱们见下文。
HBase 的读取工作开始之前须要构建初始的 Scanner 体系,波及 RegionScanner 与 StoreScanner 的对象初始化,咱们具体来看:
1)RegionScanner 对象的初始化:
1. 建设 RegionScanner 对象,筹备开始 Scan 工作波及的所有 Scanner 的生成。
2. 依据 scan 工作波及的所有 column family,在本 region 上别离会为其中的每个 column family 生成一个 StoreScanner。如果开启了 on-demand column family loading,那么会依据传入 FilterList 的 isFamilyEssential 办法进行判断,如果 isFamilyEssential,那么会将该 StoreScanner 放入 storeHeap 中,否则放入 joinedHeap 中。
3.storeHeap 和 joinedHeap 中寄存 StoreScanner 的模式为 PriorityQueue,优先级为 CellComparatorImp。
2)StoreScanner 对象的初始化
接下来咱们介绍 RegionScanner 对象的初始化中,咱们一笔带过的 StoreScanner 的生成过程:
1. 依据 scan.isReversed()管制 StoreScanner 中的 Scanner 的优先级程序。
2. 依据传入的 scan 信息,生成 matcher 内置对象,该对象在查问过程中会对 StoreScanner 读取的数据进行一个筛选。
3. 依据 scan 信息 startRow,stopRow 在 storeEngine 中查问出波及的 HStoreFile,对这些 HStoreFile 别离建设 StoreFileScanner,组成 scannerList,并且以 StoreFileComparators.SEQ_ID 为优先级(maxSequenceId 升序,FileSize 降序,BulkTime 升序,PathName 升序)。
4. 对 scannerList 依据 timestamp range, row key range, bloomFilter 做一个过滤。
5.scannerList 中残余的 scanner 依据 startRow,stopRow 将指针 seek 到正确的地位。
6. 将 scanners 以 PriorityQueue 的模式组织,优先级同样为 CellComparatorImp。
PS:StoreFileComparators.SEQ_ID —— Comparator.comparingLong(HStoreFile::getMaxSequenceId) .thenComparing(Comparator.comparingLong(new GetFileSize()).reversed()) .thenComparingLong(new GetBulkTime()).thenComparing(new GetPathName())
组建好须要 Scanner 体系之后,后续就是读取流程了。
读取流程如下图所示:
RegionScanner 次要负责以下性能:
其蕴含 storeHeap 与 joinedHeap 都为 KeyValueHeap 的对象实例,heap 底层是蕴含了多个 StoreScanner 组成的 PriorityQueue,comparator = CellComparatorImp。向上提供符合条件的整行数据的迭代查问。
1. 循环从 storeHeap 上获取 cell 数据,以此判断是否还存在待获取数据。如果没有,return false。如果有:
2. 那么先从 storeHeap 上获取 family essential 相干的数据,应用 filter 进行过滤。如果被过滤,continue loop。如果没有:
3. 那么从 joinedHeap 上获取残余数据,返回。
StoreScanner 次要负责以下性能:
StoreScanner 尽管是实现了 KeyValueScanner 和 InternalScanner 的类,但次要靠其成员变量 heap(KeyValueHeap)来实现必要的操作。heap 由多个 StoreFileScanner 实例依照 PriorityQueue 组成,comparator = CellComparatorImp。
1. 循环从 heap 中获取 cell。
2. 通过 matcher 匹配 cell 取得返回的 MatchCode,不同 MatchCode 会触发不同的操作,见下表。
3. 不停循环,直到数据组成整行,向上返回。
StoreScanner 中 KeyValueHeap 的 next 性能:
storeScanner 中的 heap.next()到底做了什么?简略来说,做了以下两件事件:1)从 current(以后的 StoreFileScanner,不在 heap 中)获取 cell 返回。2)更新以后 current,把 current 放回 heap 从新排序,再获取以后最优先的 StoreFileScanner 作为 current。
具体做法如下:
1. 从以后的 StoreFileScanner current 中获取下一个 cell(kvReturn)。再获取 kvReturn 往后的第一个 cell(kvNext)
2. 判断 kvNext 是否为空。为空代表以后 current 读取结束,须要从 heap 中获取下一个 scanner 记为 current。不为空则
3. 从以后 heap 中获取第一个 scanner,与 current 进行比照。判断它们谁通过 peek()取得的 cell key 最小,如果 scanner 更小,那么把 current 放回 heap。从新 heap.poll()取得最新 current。
4. 返回 kvReturn cell。
至此整个 HBase 的读路径分析完结,留待补充的点:
1.Matcher 的实现逻辑剖析。
2.BloomFilter 的过滤剖析。
3.StoreFileScanner 以下直到 HDFS 之间的链路剖析,两头波及一个 BlockCache。
原文链接
本文为阿里云原创内容,未经容许不转载。