KeyValue and HFile create

来源:互联网 发布:国防强固必须优化 编辑:程序博客网 时间:2024/04/27 21:14

HBase put数据时会先将数据写入内存,其内存结构是一个ConcurrentSkipListMap,其Comparator是KVComparator。

keyvalue对象结构

KVComparator的KeyValue对象比较过程

1.使用KeyComparator比较rowkey,结果是rowkey字节序从小到大

2.如果rowkey一样,则按column family比较,结果是column family字节序从小到大

3.如果column family一样,则按family+qualifier比较,结果是qualifier字节序从小到大

4.如果qualifier也一样,则按timestamp排序,结果是timestamp从大到小排序

5.如果timestamp也一样,则按type排序,delete在put之前

6.以上都一样,则按照memstoreTS排序,memstoreTS是原子递增id,不可能一样,结果是memstoreTS从大到小排序,越新的修改会排前面,方便scan

可见KeyValue对象在内存里其实是已经排序好了,flush生成文件的时候,只是简单的scan一下,设置maxVersion(在这里超过maxVersion的put自动失效了),将每个KeyValue对象写入HDFS

Flush生成HFile的过程大抵如下

1.构造Writer,最新版本是HFileWriterV2,第2版

2.循环将KeyValue对象append到writer,这里会按block写入cache,默认64k,每64k,就要重新new一个block,每次finish一个block,就会添加一条索引记录到block index,到block index超过一定限制(默认124K),则写入一个特殊的InlineBlock,代表这是一个索引块,HFile就是data block和inline block交替结构

3.KeyValue对象写完后,再将索引数据以inline block的形式全部写入,最后写入root index,fileInfo等信息。

HFile V2结构

其实现类图如下

 

主流程

Java代码  收藏代码
  1. Scan scan = new Scan();  
  2.     //最多保留的版本,默认为3  
  3.     scan.setMaxVersions(scanInfo.getMaxVersions());  
  4.     // Use a store scanner to find which rows to flush.  
  5.     // Note that we need to retain deletes, hence  
  6.     // treat this as a minor compaction.  
  7.     InternalScanner scanner = new StoreScanner(this, scan, Collections  
  8.         .singletonList(new CollectionBackedScanner(set, this.comparator)),  
  9.         ScanType.MINOR_COMPACT, this.region.getSmallestReadPoint(),  
  10.         HConstants.OLDEST_TIMESTAMP);  
  11.     try {  
  12.       // TODO:  We can fail in the below block before we complete adding this  
  13.       // flush to list of store files.  Add cleanup of anything put on filesystem  
  14.       // if we fail.  
  15.       synchronized (flushLock) {  
  16.         status.setStatus("Flushing " + this + ": creating writer");  
  17.         // A. Write the map out to the disk  
  18.         writer = createWriterInTmp(set.size());  
  19.         writer.setTimeRangeTracker(snapshotTimeRangeTracker);  
  20.         pathName = writer.getPath();  
  21.         try {  
  22.           List<KeyValue> kvs = new ArrayList<KeyValue>();  
  23.           boolean hasMore;  
  24.           do {  
  25.             hasMore = scanner.next(kvs);  
  26.             if (!kvs.isEmpty()) {  
  27.               for (KeyValue kv : kvs) {  
  28.                 // If we know that this KV is going to be included always, then let us  
  29.                 // set its memstoreTS to 0. This will help us save space when writing to disk.  
  30.                 if (kv.getMemstoreTS() <= smallestReadPoint) {  
  31.                   // let us not change the original KV. It could be in the memstore  
  32.                   // changing its memstoreTS could affect other threads/scanners.  
  33.                   kv = kv.shallowCopy();  
  34.                   kv.setMemstoreTS(0);  
  35.                 }  
  36.         //append写keyvalue  
  37.                 writer.append(kv);  
  38.                 flushed += this.memstore.heapSizeChange(kv, true);  
  39.               }  
  40.               kvs.clear();  
  41.             }  
  42.           } while (hasMore);  
  43.         } finally {  
  44.           // Write out the log sequence number that corresponds to this output  
  45.           // hfile.  The hfile is current up to and including logCacheFlushId.  
  46.           status.setStatus("Flushing " + this + ": appending metadata");  
  47.           writer.appendMetadata(logCacheFlushId, false);  
  48.           status.setStatus("Flushing " + this + ": closing flushed file");  
  49.     //写入元数据  
  50.           writer.close();  
  51.         }  
  52.       }  
  53.     } finally {  
  54.       flushedSize.set(flushed);  
  55.       scanner.close();  
  56.     }  

 append过程

Java代码  收藏代码
  1. private void append(final long memstoreTS, final byte[] key, final int koffset, final int klength,  
  2.       final byte[] value, final int voffset, final int vlength)  
  3.       throws IOException {  
  4.     //检查key顺序,返回是否和上一个key重复  
  5.     boolean dupKey = checkKey(key, koffset, klength);  
  6.     checkValue(value, voffset, vlength);  
  7.     //如果是新key,才检查block是否超过限制,也就是说同样的key保证在同一个block data里  
  8.     //如果block超过64K限制,则开始将block写入HDFS的outputstream,不flush,同时更新block index信息  
  9.     if (!dupKey) {  
  10.       checkBlockBoundary();  
  11.     }  
  12.     //初始化的时候重置状态,准备开始写入data block了  
  13.     if (!fsBlockWriter.isWriting())  
  14.       newBlock();  
  15.   
  16.     // Write length of key and value and then actual key and value bytes.  
  17.     // Additionally, we may also write down the memstoreTS.  
  18.     {  
  19.     //userDataStream是临时的,如果block满了之后,会将里面的数据flush到HDFS的outputstream  
  20.     //这里将keyvalue对象顺序写入  
  21.       DataOutputStream out = fsBlockWriter.getUserDataStream();  
  22.       out.writeInt(klength);  
  23.       totalKeyLength += klength;  
  24.       out.writeInt(vlength);  
  25.       totalValueLength += vlength;  
  26.       out.write(key, koffset, klength);  
  27.       out.write(value, voffset, vlength);  
  28.       if (this.includeMemstoreTS) {  
  29.         WritableUtils.writeVLong(out, memstoreTS);  
  30.       }  
  31.     }  
  32.   
  33.     // Are we the first key in this block?  
  34.     //block的第一个key,后续会作为data index的entry属性  
  35.     if (firstKeyInBlock == null) {  
  36.       // Copy the key.  
  37.       firstKeyInBlock = new byte[klength];  
  38.       System.arraycopy(key, koffset, firstKeyInBlock, 0, klength);  
  39.     }  
  40.     //上一个keyvalue  
  41.     lastKeyBuffer = key;  
  42.     lastKeyOffset = koffset;  
  43.     lastKeyLength = klength;  
  44.     entryCount++;  
  45.   }  

 初始化开始写入

Java代码  收藏代码
  1. /** 
  2.      * Starts writing into the block. The previous block's data is discarded. 
  3.      * 
  4.      * @return the stream the user can write their data into 
  5.      * @throws IOException 
  6.      */  
  7.     public DataOutputStream startWriting(BlockType newBlockType)  
  8.         throws IOException {  
  9.       if (state == State.BLOCK_READY && startOffset != -1) {  
  10.         // We had a previous block that was written to a stream at a specific  
  11.         // offset. Save that offset as the last offset of a block of that type.  
  12.     //保存着同类型block的上一个block的偏移量  
  13.         prevOffsetByType[blockType.getId()] = startOffset;  
  14.       }  
  15.     //当前block的偏移量  
  16.       startOffset = -1;  
  17.     //block类型,主要有data,block,index block,meta block  
  18.       blockType = newBlockType;  
  19.     //临时buffer  
  20.       baosInMemory.reset();  
  21.     //头数据,这里是占位用,后续finish block的时候会写入正式的header数据  
  22.       baosInMemory.write(DUMMY_HEADER);  
  23.     //开始写  
  24.       state = State.WRITING;  
  25.   
  26.       // We will compress it later in finishBlock()  
  27.     //临时stream  
  28.       userDataStream = new DataOutputStream(baosInMemory);  
  29.       return userDataStream;  
  30.     }  

 写着写着,可能block就满了,检查data block是否已满

Java代码  收藏代码
  1. private void checkBlockBoundary() throws IOException {  
  2. //默认64K  
  3.    if (fsBlockWriter.blockSizeWritten() < blockSize)  
  4.      return;  
  5. //将之前写入的userDataStream里的data block写入HDFS的outputStream,添加索引记录      
  6.    finishBlock();  
  7. //如果索引快满了,则将index block写入HDFS的outputStream  
  8.    writeInlineBlocks(false);  
  9. //重置状态,进入’WRITING‘状态,等待写入  
  10.    newBlock();  
  11.  }  

 具体finishBlock过程,flush数据到HDFS的outputstream

Java代码  收藏代码
  1. /** Clean up the current block */  
  2.  private void finishBlock() throws IOException {  
  3. //前置状态’WRITING‘,userDataStream有数据写入  
  4.    if (!fsBlockWriter.isWriting() || fsBlockWriter.blockSizeWritten() == 0)  
  5.      return;  
  6.   
  7.    long startTimeNs = System.nanoTime();  
  8.   
  9.    // Update the first data block offset for scanning.  
  10. //第一个data block的偏移量  
  11.    if (firstDataBlockOffset == -1) {  
  12.      firstDataBlockOffset = outputStream.getPos();  
  13.    }  
  14.   
  15.    // Update the last data block offset  
  16. //上一个data block的偏移量  
  17.    lastDataBlockOffset = outputStream.getPos();  
  18.   
  19. //这里将userDataStream里的数据flush到HDFS的outputStream  
  20.    fsBlockWriter.writeHeaderAndData(outputStream);  
  21. //在HDFS上写了多少字节,有可能压缩后,加上了checksum数据  
  22.    int onDiskSize = fsBlockWriter.getOnDiskSizeWithHeader();  
  23. //更新data block的索引  
  24.    dataBlockIndexWriter.addEntry(firstKeyInBlock, lastDataBlockOffset,  
  25.        onDiskSize);  
  26. //未压缩的数据字节数  
  27.    totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();  
  28.   
  29.    HFile.offerWriteLatency(System.nanoTime() - startTimeNs);  
  30.    HFile.offerWriteData(onDiskSize);  
  31. //更新block cache      
  32.    if (cacheConf.shouldCacheDataOnWrite()) {  
  33.      doCacheOnWrite(lastDataBlockOffset);  
  34.    }  
  35.  }  

 写入HDFS stream过程

Java代码  收藏代码
  1. public void writeHeaderAndData(FSDataOutputStream out) throws IOException {  
  2.       long offset = out.getPos();  
  3.       if (startOffset != -1 && offset != startOffset) {  
  4.         throw new IOException("A " + blockType + " block written to a "  
  5.             + "stream twice, first at offset " + startOffset + ", then at "  
  6.             + offset);  
  7.       }  
  8.     //这个块的开始位置  
  9.       startOffset = offset;  
  10.     //写  
  11.       writeHeaderAndData((DataOutputStream) out);  
  12.     }  
  13.   
  14.     private void writeHeaderAndData(DataOutputStream out) throws IOException {  
  15.     //这个方法比较重要,当一个block需要flush到HDFS stream的时候,需要做数据做一些处理,比如压缩,编码等,设置状态为’READY‘  
  16.       ensureBlockReady();  
  17.     //onDiskBytesWithHeader是处理之后的数据,直接写入HDFS stream  
  18.       out.write(onDiskBytesWithHeader);  
  19.       if (compressAlgo == NONE) {  
  20.         if (onDiskChecksum == HConstants.EMPTY_BYTE_ARRAY) {  
  21.           throw new IOException("A " + blockType   
  22.               + " without compression should have checksums "   
  23.               + " stored separately.");  
  24.         }  
  25.     //不压缩的话,还要写入checksum  
  26.         out.write(onDiskChecksum);  
  27.       }  
  28.     }  

 对buffer数据处理部分,包括压缩和编码等处理

Java代码  收藏代码
  1. private void finishBlock() throws IOException {  
  2.     //先flush一下  
  3.       userDataStream.flush();  
  4.   
  5.       // This does an array copy, so it is safe to cache this byte array.  
  6.     //拿到buffer中的数据,也就是当前所有写入的数据,未压缩  
  7.       uncompressedBytesWithHeader = baosInMemory.toByteArray();  
  8.     //上一个同类型的block偏移量  
  9.       prevOffset = prevOffsetByType[blockType.getId()];  
  10.   
  11.       // We need to set state before we can package the block up for  
  12.       // cache-on-write. In a way, the block is ready, but not yet encoded or  
  13.       // compressed.  
  14.     //READY,准备flush  
  15.       state = State.BLOCK_READY;  
  16.     //encode  
  17.       encodeDataBlockForDisk();  
  18.     //压缩和checksum  
  19.       doCompressionAndChecksumming();  
  20.     }  

 压缩和checksum,压缩之后,checksum数据直接写入onDiskBytesWithHeader,否则写入onDiskChecksum,不管压缩不压缩,都要写入block的header数据

Java代码  收藏代码
  1. private void doCompressionAndChecksumming() throws IOException {  
  2.       // do the compression  
  3.       if (compressAlgo != NONE) {  
  4.         compressedByteStream.reset();  
  5.         compressedByteStream.write(DUMMY_HEADER);  
  6.   
  7.         compressionStream.resetState();  
  8.   
  9.         compressionStream.write(uncompressedBytesWithHeader, HEADER_SIZE,  
  10.             uncompressedBytesWithHeader.length - HEADER_SIZE);  
  11.   
  12.         compressionStream.flush();  
  13.         compressionStream.finish();  
  14.   
  15.         // generate checksums  
  16.         onDiskDataSizeWithHeader = compressedByteStream.size(); // data size  
  17.   
  18.         // reserve space for checksums in the output byte stream  
  19.         ChecksumUtil.reserveSpaceForChecksums(compressedByteStream,   
  20.           onDiskDataSizeWithHeader, bytesPerChecksum);  
  21.   
  22.   
  23.         onDiskBytesWithHeader = compressedByteStream.toByteArray();  
  24.         putHeader(onDiskBytesWithHeader, 0, onDiskBytesWithHeader.length,  
  25.             uncompressedBytesWithHeader.length, onDiskDataSizeWithHeader);  
  26.   
  27.        // generate checksums for header and data. The checksums are  
  28.        // part of onDiskBytesWithHeader itself.  
  29.        ChecksumUtil.generateChecksums(  
  30.          onDiskBytesWithHeader, 0, onDiskDataSizeWithHeader,  
  31.          onDiskBytesWithHeader, onDiskDataSizeWithHeader,  
  32.          checksumType, bytesPerChecksum);  
  33.   
  34.         // Checksums are already part of onDiskBytesWithHeader  
  35.         onDiskChecksum = HConstants.EMPTY_BYTE_ARRAY;  
  36.   
  37.         //set the header for the uncompressed bytes (for cache-on-write)  
  38.         putHeader(uncompressedBytesWithHeader, 0,  
  39.           onDiskBytesWithHeader.length + onDiskChecksum.length,  
  40.           uncompressedBytesWithHeader.length, onDiskDataSizeWithHeader);  
  41.   
  42.       } else {  
  43.         // If we are not using any compression, then the  
  44.         // checksums are written to its own array onDiskChecksum.  
  45.         onDiskBytesWithHeader = uncompressedBytesWithHeader;  
  46.   
  47.         onDiskDataSizeWithHeader = onDiskBytesWithHeader.length;  
  48.     //check份数  
  49.         int numBytes = (int)ChecksumUtil.numBytes(  
  50.                           uncompressedBytesWithHeader.length,  
  51.                           bytesPerChecksum);  
  52.     //checksum数据  
  53.         onDiskChecksum = new byte[numBytes];  
  54.   
  55.         //set the header for the uncompressed bytes  
  56.     //修改header  
  57.         putHeader(uncompressedBytesWithHeader, 0,  
  58.           onDiskBytesWithHeader.length + onDiskChecksum.length,  
  59.           uncompressedBytesWithHeader.length, onDiskDataSizeWithHeader);  
  60.   
  61.         ChecksumUtil.generateChecksums(  
  62.           uncompressedBytesWithHeader, 0, uncompressedBytesWithHeader.length,  
  63.           onDiskChecksum, 0,  
  64.           checksumType, bytesPerChecksum);  
  65.       }  
  66.     }  

 data block处理完之后,更新索引,索引项由block的firstkey,开始的偏移量,dataSize组成。索引主要有2种,leaf-level chunk和root index chunk

Java代码  收藏代码
  1.   void add(byte[] firstKey, long blockOffset, int onDiskDataSize,  
  2.        long curTotalNumSubEntries) {  
  3.      // Record the offset for the secondary index  
  4. //二级索引,记每个entry的偏移量  
  5.      secondaryIndexOffsetMarks.add(curTotalNonRootEntrySize);  
  6. //下一个entry的偏移地址  
  7.      curTotalNonRootEntrySize += SECONDARY_INDEX_ENTRY_OVERHEAD  
  8.          + firstKey.length;  
  9. //给root chunk用  
  10.      curTotalRootSize += Bytes.SIZEOF_LONG + Bytes.SIZEOF_INT  
  11.          + WritableUtils.getVIntSize(firstKey.length) + firstKey.length;  
  12. //索引信息记录  
  13.      blockKeys.add(firstKey);  
  14.      blockOffsets.add(blockOffset);  
  15.      onDiskDataSizes.add(onDiskDataSize);  
  16. //如果是root index chunk添加索引  
  17.      if (curTotalNumSubEntries != -1) {  
  18.        numSubEntriesAt.add(curTotalNumSubEntries);  
  19.   
  20.        // Make sure the parallel arrays are in sync.  
  21.        if (numSubEntriesAt.size() != blockKeys.size()) {  
  22.          throw new IllegalStateException("Only have key/value count " +  
  23.              "stats for " + numSubEntriesAt.size() + " block index " +  
  24.              "entries out of " + blockKeys.size());  
  25.        }  
  26.      }  
  27.    }  

 回到前头,finishBlock之后,数据都从buffer中flush到了HDFS的stream里。这个时候给index block一个机会,检查下是否已满,满的话,将索引块flush到HDFS

Java代码  收藏代码
  1.  private void writeInlineBlocks(boolean closing) throws IOException {  
  2.    for (InlineBlockWriter ibw : inlineBlockWriters) {  
  3. //如果InlineBlock需要flush,就flush  
  4.      while (ibw.shouldWriteBlock(closing)) {  
  5.        long offset = outputStream.getPos();  
  6.        boolean cacheThisBlock = ibw.cacheOnWrite();  
  7. //和data block写入一样,先start,再write  
  8.        ibw.writeInlineBlock(fsBlockWriter.startWriting(  
  9.            ibw.getInlineBlockType()));  
  10.        fsBlockWriter.writeHeaderAndData(outputStream);  
  11.        ibw.blockWritten(offset, fsBlockWriter.getOnDiskSizeWithHeader(),  
  12.            fsBlockWriter.getUncompressedSizeWithoutHeader());  
  13.        totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();  
  14.   
  15.        if (cacheThisBlock) {  
  16.          doCacheOnWrite(offset);  
  17.        }  
  18.      }  
  19.    }  
  20.  }  

 对于BlockIndexWriter来说规则是,maxChunkSize默认128K

Java代码  收藏代码
  1. curInlineChunk.getNonRootSize() >= maxChunkSize;  

 看看BlockIndexWriter是如何写的

Java代码  收藏代码
  1. public void writeInlineBlock(DataOutput out) throws IOException {  
  2.      if (singleLevelOnly)  
  3.        throw new UnsupportedOperationException(INLINE_BLOCKS_NOT_ALLOWED);  
  4.   
  5.      // Write the inline block index to the output stream in the non-root  
  6.      // index block format.  
  7. //以leaf chunk写入  
  8.      curInlineChunk.writeNonRoot(out);  
  9.   
  10.      // Save the first key of the inline block so that we can add it to the  
  11.      // parent-level index.  
  12. //保留这个index block的firstkey,给后续多级索引用  
  13.      firstKey = curInlineChunk.getBlockKey(0);  
  14.   
  15.      // Start a new inline index block  
  16. //curInlineChunk初始化,后续索引数据继续写  
  17.      curInlineChunk.clear();  
  18.    }  

 写入leaf chunk

Java代码  收藏代码
  1.  void writeNonRoot(DataOutput out) throws IOException {  
  2.      // The number of entries in the block.  
  3. //从索引记录数  
  4.      out.writeInt(blockKeys.size());  
  5.   
  6.      if (secondaryIndexOffsetMarks.size() != blockKeys.size()) {  
  7.        throw new IOException("Corrupted block index chunk writer: " +  
  8.            blockKeys.size() + " entries but " +  
  9.            secondaryIndexOffsetMarks.size() + " secondary index items");  
  10.      }  
  11.   
  12.      // For each entry, write a "secondary index" of relative offsets to the  
  13.      // entries from the end of the secondary index. This works, because at  
  14.      // read time we read the number of entries and know where the secondary  
  15.      // index ends.  
  16. //二级索引数据写入,因为每个索引entry是变长的,这个二级索引记录着每个entry的偏移信息,方便查找  
  17.      for (int currentSecondaryIndex : secondaryIndexOffsetMarks)  
  18.        out.writeInt(currentSecondaryIndex);  
  19.   
  20.      // We include one other element in the secondary index to calculate the  
  21.      // size of each entry more easily by subtracting secondary index elements.  
  22. //总大小  
  23.      out.writeInt(curTotalNonRootEntrySize);  
  24. //索引数据  
  25.      for (int i = 0; i < blockKeys.size(); ++i) {  
  26.        out.writeLong(blockOffsets.get(i));  
  27.        out.writeInt(onDiskDataSizes.get(i));  
  28.        out.write(blockKeys.get(i));  
  29.      }  
  30.    }  

 索引block写入HDFS stream后,更新rootChunk索引,rootChunk是一个对data block index块的索引结构,所有keyvalue都写完后,rootChunk才会flush

到HDFS stream,会进一步分裂多级结构,但是在循环写入的时候只有2级

Java代码  收藏代码
  1. public void blockWritten(long offset, int onDiskSize, int uncompressedSize)  
  2.    {  
  3.      // Add leaf index block size  
  4.      totalBlockOnDiskSize += onDiskSize;  
  5.      totalBlockUncompressedSize += uncompressedSize;  
  6.   
  7.      if (singleLevelOnly)  
  8.        throw new UnsupportedOperationException(INLINE_BLOCKS_NOT_ALLOWED);  
  9.   
  10.      if (firstKey == null) {  
  11.        throw new IllegalStateException("Trying to add second-level index " +  
  12.            "entry with offset=" + offset + " and onDiskSize=" + onDiskSize +  
  13.            "but the first key was not set in writeInlineBlock");  
  14.      }  
  15. //写入的时候,只有2级,因为rootChunk 128K足够存memstore的所有索引信息了  
  16.      if (rootChunk.getNumEntries() == 0) {  
  17.        // We are writing the first leaf block, so increase index level.  
  18.        expectNumLevels(1);  
  19.        numLevels = 2;  
  20.      }  
  21.   
  22.      // Add another entry to the second-level index. Include the number of  
  23.      // entries in all previous leaf-level chunks for mid-key calculation.  
  24. //root索引写入entry,totalNumEntries为当前子节点数,也就是leaf level chunk数目  
  25.      rootChunk.add(firstKey, offset, onDiskSize, totalNumEntries);  
  26.      firstKey = null;  
  27.    }  

 以上就是循环的大抵过程,data block和data index block交替写入。当所有数据都写完后,开始做close操作,看HFileWriterV2的close操作

Java代码  收藏代码
  1. public void close() throws IOException {  
  2.     if (outputStream == null) {  
  3.       return;  
  4.     }  
  5.     // Write out the end of the data blocks, then write meta data blocks.  
  6.     // followed by fileinfo, data block index and meta block index.  
  7.     //结尾数据写入stream  
  8.     finishBlock();  
  9.     //写index block数据  
  10.     writeInlineBlocks(true);  
  11.     //trailer信息  
  12.     FixedFileTrailer trailer = new FixedFileTrailer(2,   
  13.                                  HFileReaderV2.MAX_MINOR_VERSION);  
  14.   
  15.     // Write out the metadata blocks if any.  
  16.     //meta block写入,V2里meta没用,V1里是用来存bloomfilter的  
  17.     if (!metaNames.isEmpty()) {  
  18.       for (int i = 0; i < metaNames.size(); ++i) {  
  19.         // store the beginning offset  
  20.         long offset = outputStream.getPos();  
  21.         // write the metadata content  
  22.         DataOutputStream dos = fsBlockWriter.startWriting(BlockType.META);  
  23.         metaData.get(i).write(dos);  
  24.   
  25.         fsBlockWriter.writeHeaderAndData(outputStream);  
  26.         totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();  
  27.   
  28.         // Add the new meta block to the meta index.  
  29.         metaBlockIndexWriter.addEntry(metaNames.get(i), offset,  
  30.             fsBlockWriter.getOnDiskSizeWithHeader());  
  31.       }  
  32.     }  
  33.   
  34.     // Load-on-open section.  
  35.   
  36.     // Data block index.  
  37.     //  
  38.     // In version 2, this section of the file starts with the root level data  
  39.     // block index. We call a function that writes intermediate-level blocks  
  40.     // first, then root level, and returns the offset of the root level block  
  41.     // index.  
  42.   
  43.     //这里将rootChunk分裂成子树,如果rootChunk很大,按照128K分裂成子block并写入HDFS,如果一开始索引树有2层的话,结果索引树会有3层  
  44.     //也就是多了一个中间层  
  45.     long rootIndexOffset = dataBlockIndexWriter.writeIndexBlocks(outputStream);  
  46.     trailer.setLoadOnOpenOffset(rootIndexOffset);  
  47.   
  48.     // Meta block index.  
  49.     //写入meta block  
  50.     metaBlockIndexWriter.writeSingleLevelIndex(fsBlockWriter.startWriting(  
  51.         BlockType.ROOT_INDEX), "meta");  
  52.     fsBlockWriter.writeHeaderAndData(outputStream);  
  53.     totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();  
  54.   
  55.     if (this.includeMemstoreTS) {  
  56.       appendFileInfo(MAX_MEMSTORE_TS_KEY, Bytes.toBytes(maxMemstoreTS));  
  57.       appendFileInfo(KEY_VALUE_VERSION, Bytes.toBytes(KEY_VALUE_VER_WITH_MEMSTORE));  
  58.     }  
  59.     //写file info  
  60.     // File info  
  61.     writeFileInfo(trailer, fsBlockWriter.startWriting(BlockType.FILE_INFO));  
  62.     fsBlockWriter.writeHeaderAndData(outputStream);  
  63.     totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();  
  64.   
  65.     // Load-on-open data supplied by higher levels, e.g. Bloom filters.  
  66.     for (BlockWritable w : additionalLoadOnOpenData){  
  67.       fsBlockWriter.writeBlock(w, outputStream);  
  68.       totalUncompressedBytes += fsBlockWriter.getUncompressedSizeWithHeader();  
  69.     }  
  70.   
  71.     //写trailer  
  72.     // Now finish off the trailer.  
  73.     trailer.setNumDataIndexLevels(dataBlockIndexWriter.getNumLevels());  
  74.     trailer.setUncompressedDataIndexSize(  
  75.         dataBlockIndexWriter.getTotalUncompressedSize());  
  76.     trailer.setFirstDataBlockOffset(firstDataBlockOffset);  
  77.     trailer.setLastDataBlockOffset(lastDataBlockOffset);  
  78.     trailer.setComparatorClass(comparator.getClass());  
  79.     trailer.setDataIndexCount(dataBlockIndexWriter.getNumRootEntries());  
  80.   
  81.   
  82.     finishClose(trailer);  
  83.   
  84.     fsBlockWriter.releaseCompressor();  
  85.   }  
0 0
原创粉丝点击