HBase Scan过程解析

转载自:http://zlx19900228.iteye.com/blog/1178233

(1) 卖家导出订单部分: 
卖家导出订单部分的表结构设计: 
Rowkey: seller_id+ gmt_create+ parent_id+biz_order_id 
Family:s quality:s value: 93个字段的String串,并且做gzip压缩。 
Hbase中的参数设置: 
maxVersions:1  Blocksize:128*1024  CompressionType:lzo 
BloomFilterType: ROW 
业务逻辑:is_main=1,is_detail=0则biz_order_id=0 
卖家导出订单只会用到hbase的scan,所以先分析hbase的scan过程,首先设置Scan的参数,这里主要用到了(cacheing,startRow,endRow,Filter,column,maxVersions) 
cacheing是设置需要缓存的result的个数,startRow设置的是scan开始的rowKey,endRow是设置的结束的rowKey,filter过滤作用,column设置需要scan的column,具体的会结合后面分析源码介绍。设置好了之后,调用你需要scan的Htable,得到scanner,这里返回的是ClientScanner,它是一个If there are multiple regions in a table, this scanner will iterate through them all.是遍历table的scanner,与RegionScanner不同,RegionScanner是RegionScanner is an iterator through a bunch of rows in an HRegion. It is used to combine scanners from multiple Stores ,是遍历一个region中的一批rows,首先调用构造方法,传入scan,接下来调用initialize方法,通过nextScanner,因为是第一次调用,所以会获得ServerCallable,接下来调用ServerCallable的instantiateServer方法,从已经prewarm的connection缓存中取得HregionLocation以及HRegionInterface的proxy,这里的proxy就是调用的java的dynamicProxy,然后通过rpc进行调用server端的代码,这里可以参考HSF,HSF也是一个RPC框架,内部仍然是用dynamicProxy进行的调用,这里调用的其实是ScannerCallable,继承自ServerCallable,调用完成init方法之后,就会调用ScannerCallable的call方法,因为第一次调用,所以会执行 
if (scannerId != -1L && closed) { 
      close(); 
    } else if (scannerId == -1L && !closed) { 
      this.scannerId = openScanner(); 

这句代码,然后调用服务端的openScanner,根据regionName,得到HRegion,再根据region以及scan获得一个InternalScanner,实现是RegionScanner,然后将这个InternalScanner加入到server端的scanners的map中,到此,openScanner方法调用完成,返回scannerId,这个scannerId就是server端的map的key,至此,就在服务端建立了一个scanner,供客户端通过传递scannerId,rpc调用,。接下来最后一步,就是设置当前的currentRegion为初始化时得到的HRegionInfo,记录当前scan的region信息,到目前为止,初始化已经完成,得到了scanner,接下来,就是对得到的scanner进行iter,遍历了,我们的ResultScanner的实现是ClientScanner,该类实现了iterator方法,仔细看代码会在hasNext方法中调用ClientScanner的next方法,next方法中,仍然又会调用getConnection().getRegionServerWithRetries(callable);方法,这里就又会调用ScannerCallable的call方法,因为在首次调用的时候,已经初始化了scannerId,所以这里会根据scannerId去hbase调用对应的InternerScanner,调用next方法,next方法会调用刚刚通过scannerId拿到的InternerScanner,然后调用next方法,核心的是调用nextInternal方法,nextInternal, nextInternal方法会先调用byte [] currentRow = peekRow();去拿出currentRow,这个过程是一个递归的过程,调用的是KeyValueHeap,看到这里,就不得不提一下RegionScanner的初始化方法了,首先,Hregion信息已经被加载了,加载了之后,就会调用你传入的scan的cf,去从已经加载完毕的Hregion中,根据CF去定位Store,定位到了之后,调用scanners.add(store.getScanner(scan, entry.getValue()));,这里会加入StoreScanner,然后构造KeyValueHeap,这里有一个递归的过程,首先这个peek操作是操作的一个递归操作,Scanner链由上到下依次是StoreScanner-> StoreFileScanner -> MemStoreScanner,所以peek操作会由上到下的去拿row信息,Peek操作是不会改变iterator的,所以拿到currentRow之后,调用之前我们定义的一系列filter完成之后,就会调用this.storeHeap.next(results, limit - results.size());这里的limit就是我们实现设置的cached,这里会不断的调用StoreScanner的next方法,直到从StoreScanner的heap中递归的调用StoreFile拿到row. 
RegionScanner初始化过程: 
从scan中得到传入的各项参数,filter等等,接下来重要的就是根据scan传入的cf,定位到store,然后从Hregion中获得已经预先初始化好了的stores map,根据cf name作为key,拿到store信息,然后调用store的getScanner,调用StoreScanner的构造函数,根据传入的Store,Scan,以及columns,进行构造,其中会构造一个ScanQueryMatcher,接下来会调用getScanners,getScanners方法会根据scan设置的是否memOnly or filesOnly,来初始化下一级的scanner,如果没有刻意设置是否只扫描MemStore或者StoreFile,那么首先会拿到当前store中存放的StoreFiles的信息,以及cacheBlocks,会为这个store中存放的每个storeFile创建StoreFileScanner,创建方式是首先获得StoreFile的信息,接下来根据这个storeFile打开一个reader,StoreFile.Reader,接下来调用reader的getStoreFileScanner,这个构造方法中即创建了StoreFileScanner,并且将HfileScanner绑定在这个StoreFileScanner上,接下来将调用shouldSeek方法,判断是否将通过reader得到的scanner加入StoreFileScanner的scanners集合中,storeFileScanner初始化完成之后,会调用this.store.memstore.shouldSeek(scan)),看memStore中是否包含需要scan的row,如果包含,那么就在scanners list中加入MemStoreScanner,然后将这些scanners返回,然后遍历scanners,对每个scanner调用seek方法,根据matcher设置的StartKey,去seekAtOrAfter,将不匹配的scanner close掉,这个方法的具体实现,还要仔细//TODO接下来将所有匹配的scanner合并起来,构造KeyValueHeap。 

初始化好了之后,遍拿到初始化好的ClientScanner,开始遍历,这里是实现了iterator()方法,hasNext,会调用ClientScanner的next方法,如果cache.size()==0,调用getConnection().getRegionServerWithRetries(callable);得到values,这个方法,最终会调用ScannerCallable的call方法,从而最终rpc调用next方法,这个方法在server端,会根据传入的scannerId,拿到scanner,这些都是已经在openScanner初始化好了的,最终会调用nextInternal方法,该方法中,先从heap中拿出当前的currentRow,这是一个递归的过程,即openScanner初始化好的scanners,最终会落在StoreFileScanner上,拿出之后,开始调用filter进行过滤,过滤通过,执行下一步,接下来就是一个不断调用peek以及next的过程,归根到底都是顺序调用storeFileScanner,memtoreScanner的过程,直到达到设置的缓存上限,然后便会返回结果,经过过滤器过滤,以及检验是否是stopRow。 
StoreFile遍历完成,MemStore遍历完成,返回,然后根据上一个region的endKey,作为下一次的startKey,然后再去定位Hregion,HregionLocation,因为是tree结构,所以定位是查找不小于startKey的最小值,查找到之后,又继续开始遍历。 

你可能感兴趣的:(HBase架构)