• hbase hfilev2


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

    HFileV2文件

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

    文件格式通过hfile.format.version配置。默觉得2。也仅仅有2这个值在0.96可用。

    可通过cf中配置DATA_BLOCK_ENCODING配置dataBlockencoding,

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

    通过在family的配置属性中配置BLOCKSIZE,来设置hfileblock大小,默觉得65536


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

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

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

    bloomfilterblocksize通过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,也就是rowbloomfilter中的最后一个kvrow不同样,表示须要加入到bloomblock中。

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

    appendGeneralBloomfilter(kv);

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

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

    appendDeleteFamilyBloomFilter(kv);

    通过HFileWriterV2.append写入kvdatablock,

    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同样。

    我指的keyrowkey

    booleandupKey = checkKey(key, koffset, klength);

    检查value是否为null

    checkValue(value, voffset,vlength);

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

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

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

    if(!dupKey) {

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

    须要运行blockflush操作。

    checkBlockBoundary();

    }

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

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

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

    并写入blockheader信息。

    if(!fsBlockWriter.isWriting())

    newBlock();


    写入kvdatablock的缓冲区中。

    //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)记录每个blockfirstkey.

    //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 {

    检查当前的fsBlockWriter的状态非State.WRITING;或者block中的值为0。不做操作。

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

    return;


    longstartTimeNs = System.nanoTime();

    //Update the first data block offset for scanning.

    if(firstDataBlockOffset== -1) {

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

    firstDataBlockOffset= outputStream.getPos();

    }

    记录上一个block的偏移量。

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

    outputStream是每次write一个blockpos的值就会添加。

    //Update the last data block offset

    lastDataBlockOffset= outputStream.getPos();

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

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

    会写入到一个buffer中。

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

    fsBlockWriter.writeHeaderAndData(outputStream);

    intonDiskSize = fsBlockWriter.getOnDiskSizeWithHeader();


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

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

    每个block就会有一条blockindex记录。

    dataBlockIndexWriter.addEntry(indexKey,lastDataBlockOffset,onDiskSize);

    totalUncompressedBytes+= fsBlockWriter.getUncompressedSizeWithHeader();

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

    是否须要写入kvcache中。

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

    if(cacheConf.shouldCacheDataOnWrite()){

    doCacheOnWrite(lastDataBlockOffset);

    }

    }




    DataBlock的格式:

    8byte

    4byte

    4byte

    8byte

    1byte

    4byte

    4byte

    ...

    blockType

    onDiskSize+checsumSize

    unCompressedSize

    prevOffset

    checksumType

    bytesPerChecksum

    onDiskSize

    data


    BlockTypeblock类型

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

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

    4部分是上一个block的偏移号

    5部分是checksumtype的类型

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

    7部分是压缩部分的大小,但不包括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.blockIndexshouldWriteBlock主要检查大小(rootindex)是否大于128*1024(128kb),

    2.bloomFilterIndexdeleteBloomFilterIndexshouldWriteBlock

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

    block写入到HDFS中。

    blockIndexblockTypeLEAF_INDEX,

    bloomfilterblockTypeBLOOM_CHUNK


    也就是说:

    blockIndex中记录有每个dataBlockfirstKey,offset,blockSize,

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

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


    每个dataBlock进行flush后,都会强制flushbloomfilterblock.

    flushbloomfilter后,

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


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

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


    最后:

    1.在运行writer.close时,写入rootindexblock

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

    记录住全部的INTERMEDIATE_INDEXfirstkey,offset,blocksize,

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

    trailer中记录ROOTINDEXoffset.

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

    3.写入FILE_INFO。会在trailer中记录住fileInfooffset.

    4.写入trailer.


    Fileinfo中包括:

    MAX_SEQ_ID_KEY,记录hfile最大的seqid,

    MAJOR_COMPACTION_KEY,是否做过majorcompaction

    TIMERANGE,记录hfile中的timeRangeTracker.

    EARLIEST_PUT_TS,hfile中最老的timestamp

    DATA_BLOCK_ENCODING,记录hfileencoding的配置值

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

    DELETE_FAMILY_COUNT。记录有deletefamily的个数。

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

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

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


    Trailer中的内容:

    majorVersion:hfile的版本,固定的值2

    minorVersion,hfile的最大版本,3.

    loadOnOpenDataOffsetdatablockrootindexoffset

    fileInfoOffset,fileinfooffset,

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

    UncompressedDataIndexSize,Uncompressedsize总大小。

    firstDataBlockOffset。第一个blockoffset

    lastDataBlockOffset,最后一个blockoffset.

    ComparatorClassName,比較器的类名称。

    dataIndexCountrootindex中存储的index个数。

    .......





  • 相关阅读:
    html头部属性全接触
    js中的window.onload和jquery中的load区别
    关机时,自动清除运行框的记录的设置方法
    MVC问题小总结,不断更新中...
    iis6 元数据库与iis6 配置的兼容 出错问题
    MVC对异步 Controller 的支持
    SQL Server2008安装报错,解决方案
    JavaScript有5个原始类型
    ASP.NET MVC中的拦截器
    F5负载均衡
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10669873.html
Copyright © 2020-2023  润新知