【每日五分钟搞定大数据】系列,HBase第四篇算法
这一篇你能够知道,apache
HFile的内部结构?缓存
HBase读文件细粒度的过程?分布式
HBase随机读写快除了MemStore以外的缘由?oop
上一篇中提到了Hbase的数据以HFile的形式存在HDFS, 物理存储路径是:大数据
NameSpace->Table->Region->CF->HFile
这一篇咱们来讲下这个HFile,把路径从HFile开始再补充一下优化
HFile->Block->KeyValue.
顺便科普一下,HFile具体存储路径为:code
/hbase/data/<nameSpace>/<tableName>/<encoded-regionname>/<column-family>/<filename>
如何读取HFile的内容:blog
hbase org.apache.hadoop.hbase.io.hfile.HFile -f /上面的路径指定某个HFile -p
这是我作的一个思惟导图,这里面的内容就是我这章要讲的东西,有点多,你们慢慢消化。
索引
Scanned block section:扫描HFile时这个部分里面的全部block都会被读取到。
Non-scanned block section:和相面的相反,扫描HFile时不会被读取到。
Load-on-open-section:regionServer启动时就会加载这个部分的数据,不过不是最早加载。
Trailer:这个部分才是最早加载到内存的,记录了各类偏移量和版本信息。
物理分类和逻辑分类对应的关系,能够在上面的图中看到
HFile有多个大小相等的block组成,Block分为四种类型:Data Block,Index Block,Bloom Block和Meta Block。
保存了实际的数据,由多个KeyValue 组成,块大小默认为64K(由建表时建立cf时指定或者HColumnDescriptor.setBlockSize(size)),在查询数据时,以block为单位加载数据到内存。
KeyValue 的结构
这三层索引我举个栗子放在一块儿说,第一层是必需要的,也是最快的,由于它会被加载到内存中。二三根据数据量决定,若是有的话在找的时候也会加载到内存。实际上就是一步步的缩小范围,相似B+树的结构:
a,b,c,d,e f,g,h,i,j k,l,m,n,o Root Index Block 第一层:a,g,l Intermediate Level Data Index Block 第二层:a,c,e || f,h,j || k ,m,o Leaf Index Block 第三层(部分):a,b || c,d || e,f
上面的流程一共IO了三次,HBase提供了一个BlockCache,是用在第4步缓存数据块,能够有必定几率免去随后一次IO。
相关配置:
hfile.data.block.size(默认64K):一样的数据量,数据块越小,数据块越多,索引块相应的也就越多,索引层级就越深
hfile.index.block.max.size(默认128K):控制索引块的大小,索引块越小,须要的索引块越多,索引的层级越深
保存用户自定义的kv对,能够被压缩。好比booleam filter就是存在元数据块中的,该块只保留value值,key值保存在元数据索引块中。每个元数据块由块头和value值组成。能够快速判断key是都在这个HFile中。
Meta Block的索引。
不被压缩,用户也能够在这一部分添加本身的元信息。
记录了HFile的基本信息、偏移值和寻址信息
Trailer Block
另外:Bloom filter相关的Block我准备专门写一篇文章,由于Bloom filter这个东西在分布式系统中很是常见并且有用,如今须要知道的是它是用来快速判断你须要查找的rowKey是否存在于HFile中(一堆的rowKey中)。
看完上面的内容咱们就能够解决文章开始提出的问题了:
HBase读文件细粒度的过程?
HBase随机读写快除了MemStore以外的缘由?
这两个问题我一块儿回答。
0.这里从找到对应的Region开始提及,前面的过程能够看上一篇文章。
1.首先用MemStoreScanner搜索MemStore里是否有所查的rowKey(这一步在内存中,很快),
2.同时也会用Bloom Block经过必定算法过滤掉大部分必定不包含所查rowKey的HFile,
3.上面提到在RegionServer启动的时候就会把Trailer,和Load-on-open-section里的block前后加载到内存,
因此接下来会查Trailer,由于它记录了每一个HFile的偏移量,能够快速排除掉剩下的部分HFile。
4.通过上面两步,剩下的就是不多一部分的HFile了,就须要根据Index Block索引数据(这部分的Block已经在内存)快速查找rowkey所在的block的位置;
5.找到block的位置后,检查这个block是否在blockCache中,在则直接去取,若是不在的话把这个block加载到blockCache进行缓存,
当下一次再定位到这个Block的时候就不须要再进行一次IO将整个block读取到内存中。
6.最后扫描这些读到内存中的Block(可能有多个,由于有多版本),找到对应rowKey返回须要的版本。
另外,关于blockCache不少人都理解错了,这里要注意的是:
blockCache并无省去扫描定位block这一步,只是省去了最后将Block加载到内存的这一步而已。
这里又引出一个问题,若是BlockCache中有须要查找的rowKey,可是版本不是最新的,那会不会读到脏数据?
HBase是多版本共存的,有多个版本的rowKey那说明这个rowKey会存在多个Block中,其中一个已经在BlockCache中,则省去了一次IO,可是其余Block的IO是没法省去的,它们也须要加载到BlockCache,而后多版本合并,得到须要的版本返回。解决多版本的问题,也是rowKey须要先定位Block而后才去读BlockCache的缘由。