1.核心组件及流程
- scan过程总体上是分层处理的,与存储上的组织方式一致,脉络比较清晰;
- 具体来说,就是region→store→hfile/memstore,分别都有对应的scanner实现进行数据读取;
- scan请求本身设置的条件,以及server和table层面的一些参数限制,会根据需要分布在不同层次的scanner中进行处理;
2.RegionScanner
序列图:https://www.processon.com/view/link/5d5cef1de4b0145255c20846
- RegionScanner的实现类在HRegion内部定义,为RegionScannerImpl;
- 该类内部主要通过一个优先级队列(heap)和一个scanner类型的指针(current)进行驱动,不断的从StoreScanner中获取数据;
下面分步说明一下数据驱动的过程:
(图1)
(图2)
(图3)
(图4)
假设startrow和stoprow分别为r1和r3。
- 1:创建指定的各个列族对应的storeScanner,如果未指定则是全部列族;
- 2:storeScanner创建过程中,会根据startrow参数,seek到对应cell;
- 3:将这些storeScanner放入一个heap中,heap为优先级队列,比较器的compare方法中比较的是KeyValueScanner所peek到的cell大小,如图1;
- 4:全部放入到heap使其有序,poll方法得到最小的storeScanner,即storeScannerB,将其赋值给current,如图2;
- 5:调用current的next方法,该方法会获取一行的全部cell,获取完后内部已seek到下一行,如图3;
- 6:再将current放回到heap中,此时会形成新的顺序,如图4;
- 7:重复4到6步,直到新的行大于或等于r3;
3.StoreScanner
序列图:https://www.processon.com/view/link/5d5f4496e4b04e664f2ee80c
- StoreScanner的数据驱动方式与RegionScanner类似,也是使用heap和current去进行控制;
- 除了数据获取之外,该类比较重要的1个部分是数据的检查,相关逻辑封装在ScanQueryMatcher中;
- ScanQueryMatcher中主要包含2个组件:DeleteTracker和ColumnTracker,前者负责处理delete逻辑,后者负责检查当前cell的column、version及value等是否符合要求;
- 另外,在getScanners的过程中会根据keyRange、timeRange、bloomBlock等对storeFile进行过滤,以减少数据的读取;
matcher返回的code及含义说明如下:
#丢弃当前cell,继续处理下个cell
SKIP
#丢弃当前cell,并推进到下个column
SEEK_NEXT_COL
#丢弃当前cell,并推进到下个row
SEEK_NEXT_ROW
#保留当前cell,继续处理下个cell
INCLUDE
#保留当前cell,并推进到下个column
INCLUDE_AND_SEEK_NEXT_COL
#保留当前cell,并推进到下个row
INCLUDE_AND_SEEK_NEXT_ROW
#当前row已完成
DONE
#整个scan已完成
DONE_SCAN
#丢弃当前cell,并推进到下个指定的cell,由少数filter用到
SEEK_NEXT_USING_HINT
4.storeFileScanner
序列图:https://www.processon.com/view/link/5d638e4fe4b0145255c7c5e4
- storeFileScanner是真正涉及到hfile数据读取的地方,会根据rowKey,基于内存中indexBlock的数据定位到具体的dataBlock位置,以block为单位进行读取;
- 读取后的block数据在内存中以ByteBuffer的形式存在,而blockSeek方法会将这个ByteBuffer的position推动到合适位置的过程;
- 接下来,会读取一个cell的数据作为返回,使上层的storeScanner能够据此对各个storeFileScanner进行排序;
- 值得一提的是,实际实现中,还存在lazySeek的优化,大致原理是根据hfile中存储的最小time,返回一个假的cell,如果该cell都不能排在前面,那就不需要关心真实的cell是什么了,等到该cell能够排在最前面的时候,再进行realSeek,这个机制对于各个hfile按时间存在明显分界并且主要读取近期数据的场景,可以有效减少实际的数据读取量;