说明
本文参考自
HBase数据读取流程解析
hbase(二)hfile结构
目录
说明
1.HBase读取数据流程
2.疑问
2.1上述(10)中,需要对满足条件的KeyValue进行从小到大排序合并构建最小堆。
2.1.1为什么Scanner需要有小到大排序?
2.1.2HBase中KeyValue是什么样的结构?
2.1.3不同KeyValue之间如何进行大小比较?
2.2上述(11)中数据是如何从最小堆KeyValueHeap:PriorityQueue中一行行获取的?
1.HBase读取数据流程
(1)Client访问zookeeper,获取hbase:meta所在RegionServer的节点信息
(2)Client访问hbase:meta所在的RegionServer,获取hbase:meta记录的元数据后先加载到内存中,然后再从内存中根据需要查询的RowKey查询出RowKey所在的Region的相关信息(Region所在RegionServer)
(3)Client访问RowKey所在Region对应的RegionServer,发起数据读取请求
(4)RegionServer构建RegionScanner(需要查询的RowKey分布在多少个Region中就需要构建多少个RegionScanner),用于对该Region的数据检索
(5)RegionScanner构建StoreScanner(Region中有多少个Store就需要构建多少个StoreScanner,Store的数量取决于Table的ColumnFamily的数量),用于对该列族的数据检索
(6)多个StoreScanner合并构建最小堆(已排序的完全二叉树)StoreHeap:PriorityQueue<StoreScanner>
(7)StoreScanner构建一个MemStoreScanner和一个或多个StoreFileScanner(数量取决于StoreFile数量)
(8)过滤掉某些能够确定所要查询的RowKey一定不在StoreFile内的对应的StoreFileScanner或MemStoreScanner
(9)经过筛选后留下的Scanner开始做读取数据的准备,将对应的StoreFile定位到满足的RowKey的起始位置
(10)将所有的StoreFileScanner和MemStoreScanner合并构建最小堆KeyValueHeap:PriorityQueue<KeyValueScanner>,排序的规则按照KeyValue从小到大排序
(11)从KeyValueHeap:PriorityQueue<KeyValueScanner>中经过一系列筛选后一行行的得到需要查询的KeyValue。
2.疑问
2.1上述(10)中,需要对满足条件的KeyValue进行从小到大排序合并构建最小堆。
对此有三个疑问如下
- 为什么这些Scanner需要由小到大排序?
- HBase中KeyValue是什么样的结构?
- 不同KeyValue之间如何进行大小比较?
2.1.1为什么Scanner需要有小到大排序?
最直接的解释是scan的结果需要由小到大输出给用户,当然,这并不全面,最合理的解释是只有由小到大排序才能使得scan效率最高。举个简单的例子,HBase支持数据多版本,假设用户只想获取最新版本,那只需要将这些数据由最新到最旧进行排序,然后取队首元素返回就可以。那么,如果不排序,就只能遍历所有元素,查看符不符合用户查询条件
2.1.2HBase中KeyValue是什么样的结构?
(1)KeyValue由Key length、value length、key、value组成
(2)其中key又由RowKey length、RowKey、ColumnFamily length、ColumnFamily、ColumnQualifier、TimeStamp、KeyType组成
- RowKey length:RowKey长度
- RowKey:RowKey内容
- ColumnFamily length:列族长度
- ColumnFamily:列族
- ColumnQualifier:列名
- Timestamp:时间戳
- KeyType:表示类型,取值有Put/Delete/Delete Column/Delete Family
2.1.3不同KeyValue之间如何进行大小比较?
由2.1.2可知,key的内容包括RowKey、ColumnFamuly、ColumnQualifier、Timestamp、KeyType。所以keyValue的大小比较规则如下:
1.比较RowKey,RowKey越小则KeyValue越小
2.RowKey相同情况下,比较ColumnFamily,ColumnFamily越小,KeyValue越小
3.ColumnFamily相同情况下,比较ColumnQualifier,ColumnQualifier越小,KeyValue越小
4.ColumnQualifier相同情况下,比较Timestamp越大,KeyValue越小
5.Timestamp相同情况下,比较KeyType(DeleteFamily -> DeleteColumn -> Delete -> Put),KeyType级别从左到右对应的KeyValue从小到大
2.2上述(11)中数据是如何从最小堆KeyValueHeap:PriorityQueue<KeyValueScanner>中一行行获取的?
下图来自HBase数据读取流程解析
这三个Scanner组成的heap为<MemstoreScanner,StoreFileScanner2, StoreFileScanner1>,Scanner由小到大排列。查询的时候首先pop出heap的堆顶元素,即MemstoreScanner,得到keyvalue = r2:cf1:name:v3:name23的数据,拿到这个keyvalue之后,需要进行如下判定:
- 检查该KeyValue的KeyType是否是Deleted/DeletedCol等,如果是就直接忽略该列所有其他版本,跳到下列(列族)
- 检查该KeyValue的Timestamp是否在用户设定的Timestamp Range范围,如果不在该范围,忽略
- 检查该KeyValue是否满足用户设置的各种filter过滤器,如果不满足,忽略
- 检查该KeyValue是否满足用户查询中设定的版本数,比如用户只查询最新版本,则忽略该cell的其他版本;反正如果用户查询所有版本,则还需要查询该cell的其他版本。
现在假设用户查询所有版本而且该keyvalue检查通过,此时当前的堆顶元素需要执行next方法去检索下一个值,并重新组织最小堆。即图中MemstoreScanner将会指向r4,重新组织最小堆之后最小堆将会变为<StoreFileScanner2, StoreFileScanner1, MemstoreScanner>,堆顶元素变为StoreFileScanner2,得到keyvalue=r2:cf1:name:v2:name22,进行一系列判定,再next,再重新组织最小堆…
不断重复这个过程,直至一行数据全部被检索得到。继续下一行…