客户端
获得RPC链接 递归查找对应rowkey的地址 递归:table - .METE. - ROOT
HConnectionManager - locateRegionInMeta
在metatable中查找region地址:
metaLocation = locateRegion(parentTable,metaKey);
发送查询请求到客户端
服务端
一、实例化HRegion
获得Get请求,HRegionServer
public Result get(byte[] regionName,Get get){
HRegion region = getRegion(regionName);//1
return region.get(get,getLockFromId(get.getLockId()));//2
}
二、HRegion调用Get方法
1.通过name获得HRegion
2.HRegion运行get方法,检查rowkey,cf;
public Result get(final Get get,final Integer locakid)
调用HRegion内部方法get
private List<KeyValue> get(Get get,boolean withCoprocessor)
三、实例化Scan,和Region迭代器RegionScanner,迭代查询Region数据
get方法中实例化Scan,实例化迭代Region的RegionScanner
--HRegion调用instantialRegionScanner
实例化内部类RegionScannerImpl
--迭代cf,每个cf生成一个Store和负责迭代数据的StoreScanner
StoreScanner scanner = store.getScanner(scan,entry.getValue());
private List<KeyValueScanner> getScanners(Scan scan,final NavigableSet<byte[]> columns)
四、实例化StoreScanner,迭代每个cf的数据
StoreScanner的内部变量keyValueHead,是keyValueSanner的集合
protected List<KeyValueScanner> getScanners(boolean cacheBlocks,boolean isGet,boolean isCompaction)
此方法会实例化两种Scanner
StoreScanner构造方法,获得两部分scanner
List<KeyValueScanner> scanners = getScannersNoCompaction();
1.内存Scanner,负责迭代在内存中(LSM数据结构,未写到HFile中的数据)
memStoreScanners = this.memestore.getScanners()
2.HFile的Scanner,负责迭代HFile,会实例化多个StoreFileScanner
storeFiles = this.getStorefiles()
List<StoreFileScanner> sfScanners = StoreFileScanner.getScannersForStoreFiles(storeFiles,cacheBlocks,isGet,isCompaction)
返回所有keyValueScanner(内存的、文件的)
五、获得KeyValue结果
RegionScannerImpl调用Next方法
public synchronized boolean next(List<KeyValue> outResults,int limit)
boolean returnResult = nextInternal(limit)
在此方法中,生成keyvalue数据结果,通过RPC返回给客户端,查询就结束了
小结:
从分析可见,hbase的get也是一次scan请求,因为Hbase的LSMTree结果,最小单位也只是HFile(hbase布隆过滤器可以有助判断rowkey不是在某个HFile中)。
对比scan,get知道确切一个Region,limit为1;所以在region分配合理下,get速度可以到毫秒级。这里也就建议scan时,要有startrowkey,endrowkey,减少便利region数
辅助:
4 布隆过滤器(Bloom filters)
数据块索引提供了一个有效的方法,在访问一个特定的行时用来查找应该读取的HFile的数据块。但是它的效用是有限的。HFile数据块的默认大小是64KB,这个大小不能调整太多。
如果你要查找一个短行,只在整个数据块的起始行键上建立索引无法给你细粒度的索引信息。例如,如果你的行占用100字节存储空间,一个64KB的数据块包含(64 * 1024)/100 = 655.53 = ~700行,而你只能把起始行放在索引位上。你要查找的行可能落在特定数据块上的行区间里,但也不是肯定存放在那个数据块上。这有多种情况的可能,或者该行在表里不存在,或者存放在另一个HFile里,甚至在MemStore里。这些情况下,从硬盘读取数据块会带来IO开销,也会滥用数据块缓存。这会影响性能,尤其是当你面对一个巨大的数据集并且有很多并发读用户时。
布隆过滤器允许你对存储在每个数据块的数据做一个反向测试。当某行被请求时,先检查布隆过滤器看看该行是否不在这个数据块。布隆过滤器要么确定回答该行不在,要么回答它不知道。这就是为什么我们称它是反向测试。布隆过滤器也可以应用到行里的单元上。当访问某列标识符时先使用同样的反向测试。
布隆过滤器也不是没有代价。存储这个额外的索引层次占用额外的空间。布隆过滤器随着它们的索引对象数据增长而增长,所以行级布隆过滤器比列标识符级布隆过滤器占用空间要少。当空间不是问题时,它们可以帮助你榨干系统的性能潜力。
你可以在列族上打开布隆过滤器,如下所示:
hbase(main):007:0> create 'mytable',
{NAME => 'colfam1', BLOOMFILTER => 'ROWCOL'}
BLOOMFILTER参数的默认值是NONE。一个行级布隆过滤器用ROW打开,列标识符级布隆过滤器用ROWCOL打开。行级布隆过滤器在数据块里检查特定行键是否不存在,列标识符级布隆过滤器检查行和列标识符联合体是否不存在。ROWCOL布隆过滤器的开销高于ROW布隆过滤器。