一 客户端GET读
客户端GET读其实没有什么逻辑,就简单几个步骤:
1.1 connection 根据表名,行健得到从哪一个RegionServer和Region上去读
1.2 通过Region信息和和GET对象构造一个GET Request
1.3 通过RPC把GET请求提交到服务器端,以得到返回结果
二 RegionServer GET读
2.1 首先将GET请求反序列化
2.2 然后对row key 和 family进行校验
2.3 通过get请求构造一个scan对象
2.4 根据scan实例化一个RegionScanner,默认的实现是RegionScan
nerImpl。在实例化的时候会做以下几件事情:
# 设置region
# 设置caching,batch,stopRow等
# 设置readpoint
# 实例化KeyValue Heap: 一个表可能有多个列簇,每一个列簇对应Store, 所以一个Region可能有多个StoreScanner,KeyValueHeap作用就是合并多个KeyValueScanner
# 根据store 得到对应的store scanner,并且根据以下条件把store scanner添加到不同的集合中去:
=> 有filter
=>scan设置了doLoadColumnFamiliesOnDemand为true
=>设置了的filter的isFamilyEssential
如果以上条件都满足,都放入join scanner 集合中,否则放入store scanner集合中,因为以上条件一般情况下很难满足,除非有一些特殊的业务逻辑,否则一般都是store scanner
并且根据store scanner 集合 和 joined scanner集合构造2个堆:
store heap 和 joined heap
2.5 调用RegionScannerImpl#nextRaw
2.6 然后KeyValyeHeap(storeHeap)根据store scanner得到一个cell
并判断当前的row key是否是stop row key. 如果不是这里可能有很多结果,并且判断是否需要对row进行过滤,如果需要则过滤一些cells
2.7 保存结果,直到达到limit数量
2.8 返回结果
三 客户端SCAN流程
3.1 调用HTable#getScanner得到ResultScanner
3.2 如果是small scan,但是又设置了batch(limit)则不允许
3.3 设置cache
3.4 根据是否反向,是否是small scan创建不同的scanner对象
我们默认以ClientScanner为例:
会初始化一些参数,比如tableName,connection,客户端重试次数,scan的最大结果数,timeout,cache等
3.5 初始化scanner,并且构造BoundedCompletionService,包含需要执行的task,已经完成的task和线程池,利用线程池完成任务的调度与执行,并同步获取结果
3.6 ResultScanner继承自Iterable接口,那么其实现ClientScanner也会有hasNext和next方法,如果没有则继续向上查找父类,next方法就是去遍历数据
3.7 如果cache 没有数据,且scanner为closed,则返回null;如果只是cache 没有数据,则把数据加载到缓存,然后查询的时候cache有数据,则直接返回。
3.8 初始化remainingResultSize 和 countdown:
remainingResultSize:RPC调用获取的数据总大小
countdown:RPC调用获取的总行数
然后通过RPC调用去RegionServer真正获取数据。
数据缓存到cache中
注意:从这一点上看,这里有一点懒加载的味道,本地缓存有的,先从本地去,本地没有了才去服务器拿,然后再放入缓存。
四 服务器端SCAN流程
客户端通过ScannerCallable#call调用RSRPCService#scan方法,从服务器上查询数据。
4.1 首先将scan请求反序列化
4.2 判断是否客户端携带的有scannerId参数,如果有则根据客户端携带的scannerId去得到RegionScanner,否则根据客户端传递过来Region参数去得到RegionScanner
4.3 调用RegionScannerImpl#nextRaw方法,去填充数据
描述HBase中scan和get的功能以及实现的异同