简介: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。
原文链接
本文为阿里云原创内容,未经容许不转载。