版权声明:本文为博主原创文章。未经博主同意不得转载。 https://blog.csdn.net/u014393917/article/details/25508809

HFileV2文件

HFileV2文件写入通过StoreFile.Writer-->HFileWriterV2进行写入。

文件格式通过。也仅仅有2这个值在0.96可用。

可通过cf中配置DATA_BLOCK_ENCODING配置dataBlock的encoding,

可配置值:NONE,PREFIX,DIFF,FAST_DIFF,PREFIX_TREE,

通过在family的配置属性中配置

通过在family的配置属性中配置BLOOMFILTER。来设置是否启用BLOOMFILTER。默认值为ROW,可选值:NONE。ROW。ROWCOL

假设io.storefile.bloom.enabled配置的值为true,默觉得true.在writer中生成一个全局的bloomfilter的Writer

在StoreFile.Writer中生成的generalBloomFilterWriter。实现类为:CompoundBloomFilterWriter,

bloomfilter的blocksize通过io.storefile.bloom.block.size配置,默觉得128*1024(128k)

假设bloomfilter属性不是ROWCOL时。同一时候io.storefile.delete.family.bloom.enabled配置为true,默认值为true,

在StoreFile.Writer中生成的deleteFamilyBloomFilterWriter,实现类:CompoundBloomFilterWriter

writer.append操作

写HFileV2文件时,在store进行flush时,会生成StoreFile.Writer实例,通过Writer.append写入kv.

publicvoid append(finalKeyValue
kv) throws IOException {

假设是一个新的kv,也就是row与bloomfilter中的最后一个kv的row不同样,表示须要加入到bloomblock中。

此部分眼下是在一个缓冲区中。

appendGeneralBloomfilter(kv);

假设kv是删除的KV,把row加入到deletebloomfilter的block中。

此部分眼下是在一个缓冲区中。

appendDeleteFamilyBloomFilter(kv);

通过HFileWriterV2.append写入kv到datablock,

writer.append(kv);

trackTimestamps(kv);

}

HFileWriterV2.append(kv)直接调用例如以下方法:

privatevoid append(finallong
memstoreTS, finalbyte[] key,

finalint koffset,
finalint klength,

finalbyte[]value,
finalintvoffset,
finalintvlength)

throwsIOException {

检查key是否合法,首先检查上一个加入的key假设比当前的key大,表示有问题。由于hfile的写入须要排序写入。

假设当前的key比上次写入的key要小,返回值为false,假设返回值为true,表示两个key同样。

我指的key是rowkey

booleandupKey = checkKey(key, koffset,
klength);

检查value是否为null,

checkValue(value, voffset,vlength);

假设rowkey与上一次的rowkey不是同一个key时,检查hfile的block是否超过了指定的大小。

假设当前的rowkey与上一次写入的rowkey同样时,

就算是block大小超过了指定的大小。同样的rowkey的kv都会写到一个block中。

if(!dupKey) {

此处是检查fsBlockWriter中的大小是否超过了blocksize的大小。假设起过了。

须要运行block的flush操作。

checkBlockBoundary();

}

第一次进行入时,fsBlockWriter的状态为State.INIT;此时须要生成一个新的block。并设置State为State.WRITING;

在运行newBlock操作时。生成一个DataOutputStream,使用一个baosInMemory(ByteArrayOutputStream),

每个block中,basosInMemory的缓冲区是重用的。因此。每个block中都会运行baosInMemory.reset操作。

并写入block的header信息。

if(!fsBlockWriter.isWriting())

newBlock();

写入kv到datablock的缓冲区中。

//Write length of key and value and then actual key and value bytes.

//Additionally, we may also write down the memstoreTS.

{

DataOutputStream out =fsBlockWriter.getUserDataStream();

out.writeInt(klength);

totalKeyLength+= klength;

out.writeInt(vlength);

totalValueLength+= vlength;

out.write(key, koffset, klength);

out.write(value, voffset,vlength);

if(this.includeMemstoreTS){

WritableUtils.writeVLong(out,memstoreTS);

}

}

记录住此block的第一个key,firstkey主要是blockindex(leaf-level-index)记录每个block的firstkey.

//Are we the first key in this block?

if(firstKeyInBlock==
null){

//Copy the key.

firstKeyInBlock=
newbyte[klength];

System.arraycopy(key,koffset,
firstKeyInBlock,0, klength);

}

记录最后一个key的值。

lastKeyBuffer= key;

lastKeyOffset= koffset;

lastKeyLength= klength;

entryCount++;

}

flush data block数据刷新

datablock的大小默觉得65536(64k),当达到此值时,会对block进行flush操作。

在HFileWriterV2中通过append会对block进行检查。

检查是否是新的一个rowkey的值,假设是检查是否须要flush当前的block,并又一次创建一个新的block

boolean dupKey =checkKey(key, koffset, klength);

checkValue(value, voffset,vlength);

if(!dupKey) {

checkBlockBoundary();

}

检查是否达到flush的值。并进行flush操作。

privatevoid checkBlockBoundary()
throwsIOException {

检查block是否达到指定的值。

if(fsBlockWriter.blockSizeWritten()<
blockSize)

return;

对datablock进行flush操作,

finishBlock();

写入索引数据到block中。

writeInlineBlocks(false);

生成一个新的block.

newBlock();

}

finishBlock方法:

privatevoid finishBlock()
throwsIOException {

。不做操作。

if(!fsBlockWriter.isWriting()||
fsBlockWriter.blockSizeWritten()== 0)

return;

longstartTimeNs = System.nanoTime();

//Update the first data block offset for scanning.

if(firstDataBlockOffset==
-1) {

假设是第一个block,设置block的offset的值为0,也就是block的開始位置。

firstDataBlockOffset=
outputStream.getPos();

}

记录上一个block的偏移量。

主要是用来记录blockindex的一些个准备信息。

此outputStream是每次write一个block后pos的值就会添加。

//Update the last data block offset

lastDataBlockOffset=
outputStream.getPos();

设置fsBlockWriter的状态为State.BLOCK_READY;这样就能够又一次运行写入操作。

通过读取buffer中的kv的值,通过encoder对block进行操作。如profix_free等。

会写入到一个buffer中。

最后把数据写入到HDFS文件里。

fsBlockWriter.writeHeaderAndData(outputStream);

intonDiskSize =
fsBlockWriter.getOnDiskSizeWithHeader();

byte[]indexKey =
comparator.calcIndexKey(lastKeyOfPreviousBlock,firstKeyInBlock);

把当前block的key与当前block的偏移量,当前block的大写和小写入到leaflevel
index(BlockIndex)中。

每个block就会有一条block的index记录。

dataBlockIndexWriter.addEntry(indexKey,lastDataBlockOffset,onDiskSize);

totalUncompressedBytes+=
fsBlockWriter.getUncompressedSizeWithHeader();

HFile.offerWriteLatency(System.nanoTime()- startTimeNs);

是否须要写入kv到cache中。

假设是须要,写入到readcache中。

if(cacheConf.shouldCacheDataOnWrite()){

doCacheOnWrite(lastDataBlockOffset);

}

}

DataBlock的格式:

8byte

4byte

4byte

8byte

1byte

4byte

4byte

...

blockType

onDiskSize+checsumSize

unCompressedSize

prevOffset

checksumType

bytesPerChecksum

onDiskSize

data

BlockType是block类型

第二个是压缩部分下checksumsize的大小

第三部分是未压缩部分的大小

部分是上一个block的偏移号

部分是checksumtype的类型

部分是是每个checksum的字节数,默觉得16*1024

部分是压缩部分的大小,但不包括checksunsize

最后是数据部分。

写入索引的block数据,要写入的索引包括例如以下几个:

blockIndex也就是dataBlockIndexWriter的默认实现是HFileBlockIndex.BlockIndexWriter.

BloomFilterIndex,也就是CompoundBloomFilterWriter实现。

DeleteBloomFilterIndex,也就是CompoundBloomFilterWriter实现。

privatevoid writeInlineBlocks(booleanclosing)
throws IOException {

for(InlineBlockWriter ibw :
inlineBlockWriters){

while(ibw.shouldWriteBlock(closing))
{

longoffset =
outputStream.getPos();

booleancacheThisBlock = ibw.getCacheOnWrite();

ibw.writeInlineBlock(fsBlockWriter.startWriting(

ibw.getInlineBlockType()));

fsBlockWriter.writeHeaderAndData(outputStream);

ibw.blockWritten(offset,fsBlockWriter.getOnDiskSizeWithHeader(),

fsBlockWriter.getUncompressedSizeWithoutHeader());

totalUncompressedBytes+=
fsBlockWriter.getUncompressedSizeWithHeader();

if(cacheThisBlock) {

doCacheOnWrite(offset);

}

}

}

}

1.blockIndex的shouldWriteBlock主要检查大小(非rootindex)是否大于128*1024(128kb),

2.bloomFilterIndex与deleteBloomFilterIndex的shouldWriteBlock,

仅仅要bloomfilter中有值,也就是chunk中有数据。shouldWriteBlock的方法返回就为true,

把block写入到HDFS中。

blockIndex的blockType为LEAF_INDEX,

bloomfilter的blockType为BLOOM_CHUNK

也就是说:

blockIndex中记录有每个dataBlock的firstKey,offset,blockSize,

bloomFilterIndex中记录有每个(row)rowkey,(rowcol)或者rowkey与Qualifier,的hash值,

此处的hash主要是bloomfilter的相关信息。

每个dataBlock进行flush后,都会强制flush到bloomfilter的block.

在flush后bloomfilter后,

会在rootBloomFilter(bloomBlockIndexWriter)的缓冲区中记录此bloomfliter的firstkey.offset,blocksize.

在每个blockindex进行flush后,这个在datablock进行flush时不会强制flsuh,仅仅有达到指定的值时,才进行flush.

在每一次对blockindex进行flush后,会在rootindex的缓冲区中记录住此blockindex的firstkey,offset,blocksize.

最后:

1.在运行writer.close时,写入rootindex的block

假设blockindex的大小超过了128k,会把rootindex的每128k写入一个INTERMEDIATE_INDEX

记录住全部的INTERMEDIATE_INDEX的firstkey,offset,blocksize,

此处是一个反复的迭代过程,仅仅有当ROOT_INDEX。能够写入的blocksize小于128kb时,把最后一个写入为ROOT_INDEX

在trailer中记录ROOTINDEX的offset.

2.接下来写入meta,也就是root的bloomfilter的信息。

3.写入FILE_INFO。会在trailer中记录住fileInfo的offset.

4.写入trailer.

Fileinfo中包括:

MAX_SEQ_ID_KEY,记录hfile最大的seqid,

MAJOR_COMPACTION_KEY,是否做过majorcompaction。

TIMERANGE,记录hfile中的timeRangeTracker.

EARLIEST_PUT_TS,hfile中最老的timestamp

DATA_BLOCK_ENCODING,记录hfile的encoding的配置值

BLOOM_FILTER_TYPE,记录有全局的bloomfilter的类型

DELETE_FAMILY_COUNT。记录有delete的family的个数。

Hfile.LASTKEY,记录此hfile中最后一个key的值,

hfile.AVG_KEY_LEN,记录key的平均长度。

Hfile.AVG_VALUE_LEN,记录value的平均长度。

Trailer中的内容:

minorVersion,hfile的最大版本,3.

loadOnOpenDataOffset。datablockrootindex的offset

fileInfoOffset,fileinfo的offset,

numDataIndexLevels,rootindex的层级。在上面提到过的INTERMEDIATE_INDEX有几个层级。

UncompressedDataIndexSize,Uncompressedsize总大小。

firstDataBlockOffset。第一个blockoffset

lastDataBlockOffset,最后一个blockoffset.

ComparatorClassName,比較器的类名称。

dataIndexCountrootindex中存储的index个数。

.......

hbase hfilev2的更多相关文章

  1. HBASE架构解析(一)

    http://www.blogjava.net/DLevin/archive/2015/08/22/426877.html 前记 公司内部使用的是MapR版本的Hadoop生态系统,因而从MapR的官 ...

  2. hbase基本结构

    HBASE  基本结构一.overview1. hbase <=> NOSQL     不错,hbase 就是某种类型的nosql 数据库,唯一的区别就是他支持海量的数据.    hbas ...

  3. HBase架构深度解析

    原文出处: DLevin(@雪地脚印_) 前记 公司内部使用的是MapR版本的Hadoop生态系统,因而从MapR的官网看到了这篇文文章:An In-Depth Look at the HBase A ...

  4. Hbase学习02

    第2章 Apache HBase配置 本章在“入门”一章中进行了扩展,以进一步解释Apache HBase的配置. 请仔细阅读本章,特别是基本先决条件,确保您的HBase测试和部署顺利进行,并防止数据 ...

  5. HBase:分布式列式NoSQL数据库

    传统的ACID数据库,可扩展性上受到了巨大的挑战.而HBase这类系统,兼具可扩展性的同时,也提出了类SQL的接口. HBase架构组成 HBase采用Master/Slave架构搭建集群,它隶属于H ...

  6. 深入HBase架构解析(一)[转]

    前记 公司内部使用的是MapR版本的Hadoop生态系统,因而从MapR的官网看到了这篇文文章:An In-Depth Look at the HBase Architecture,原本想翻译全文,然 ...

  7. hbase源码系列(九)StoreFile存储格式

    从这一章开始要讲Region Server这块的了,但是在讲Region Server这块之前得讲一下StoreFile,否则后面的不好讲下去,这块是基础,Region Sever上面的操作,大部分都 ...

  8. 【转】HBase架构解析

    转载地址:http://www.blogjava.net/DLevin/archive/2015/08/22/426877.html HBase架构组成 HBase采用Master/Slave架构搭建 ...

  9. hbase(一)region

    前言 文章不含源码,只是一些官方资料的整理和个人理解 架构总览 这张图在大街小巷里都能看到,感觉是hbase架构中最详细最清晰的一张,稍微再补充几点. 1) Hlog是低版本hbase术语,现在称为W ...

随机推荐

  1. js创建类(封装)

    js如何创建类(封装)     学过其他面向对象语言的JavaScripter,可能都应用过类,如:class{},等定义的一系列方法, 但是初学者看是学习js的时候,经常会看到这样一句话,那就是Ja ...

  2. Java入门系列-27-反射

    咱们可能都用过 Spring AOP ,底层的实现原理是怎样的呢? 反射常用于编写工具,企业级开发要用到的 Mybatis.Spring 等框架,底层的实现都用到了反射.能用好反射,就能提高我们编码的 ...

  3. [转]Entity Framework Sprocs with Multiple Result Sets

    本文转自:https://msdn.microsoft.com/en-us/data/jj691402.aspx Entity Framework Sprocs with Multiple Resul ...

  4. FFmpeg的tutorial 学习

    一.前言: 这是一个学习 FFmpeg 的 tutorial 系列. 这个是一个对初学者比较友好的FFmpeg学习教程,作者一步步引导我们实现了一个音视频同步的播放器. 参考链接: 原文地址: htt ...

  5. Java CAS总结

    文章目录 1. CPU指令对CAS的支持(CPU的cas指令是原子的) 或许我们可能会有这样的疑问,假设存在多个线程执行CAS操作并且CAS的步骤很多,有没有可能在判断V和E相同后,正要赋值时,切换了 ...

  6. FLASK实现上传下载功能

    #!-*-coding=utf-8-*- # from flask import Flask # # app = Flask(__name__) # # # @app.route('/') # def ...

  7. JavaScript 递归法排列组合二维数组

    <html> <head> <title>二维数组排列组合</title> </head> <body> <div id= ...

  8. js event事件对象概括

    事件是用户或者浏览器自身执行的动作,而响应某个事件的函数就叫做事件处理程序或者叫事件侦听器. 定义事件处理程序可以大致分为以下三种: 一.html事件处理程序 元素支持的每种事件都可以用一个与之对应的 ...

  9. 边缘检测matlab算法汇总

    边缘检测matlab算法汇总 1.      基于一阶微分算子检测边缘图像 一阶微分边缘算子又称梯度边缘算子,它是利用图像在边缘处的阶跃性,及图像梯度在边缘去得极大值得特征性进行边缘检测. Sobel ...

  10. 01_dubbo实例_服务分组

    [为什么要服务分组?] 当一个接口有多种实现时,可以用group区分. [ Provider 的配置信息] <?xml version="1.0" encoding=&quo ...