`
BlackWing
  • 浏览: 196456 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

LoadIncrementalHFiles是copy而不是move的疑惑

阅读更多
转载请标明出处:http://blackwing.iteye.com/blog/1991901

之前在另一篇文章里实现的自定义job生成HFile并使用LoadIncrementalHFiles  入库HBase :http://blackwing.iteye.com/blog/1991380

但发现入库时,非常的慢,而且几次都失败了,明明官方教材说这个操作是move的:
The completebulkload utility will move generated StoreFiles into an HBase table. This utility is often used in conjunction with output from Section 15.1.10, “ImportTsv”.


再次验证google的强大,发现官方有这个问题的解释:
https://issues.apache.org/jira/browse/HBASE-9537
https://issues.apache.org/jira/browse/HBASE-8304


主要问题是,批量导入HBase时,如果hdfs地址没有写端口,会认为是两个不同文件系统,所以要copy,而如果当是同一文件系统时,则是move,秒级入库了2GB的文件。
指令差别如下:
copy的场景:
./hbase-0.96.0-hadoop1/bin/hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles hdfs://namenode/outputtsv gonghui_test


move的场景:
./hbase-0.96.0-hadoop1/bin/hbase org.apache.hadoop.hbase.mapreduce.LoadIncrementalHFiles hdfs://namenode:8020/outputtsv gonghui_test


剩下的需要好好看看源码增加理解了。
没有secure authentication时,调用流程如下:
LoadIncrementalHFiles.doBulkLoad(Path hfofDir, final HTable table) -->
 LoadIncrementalHFiles.bulkLoadPhase(final HTable table, final HConnection conn,ExecutorService pool, Deque<LoadQueueItem> queue,final Multimap<ByteBuffer, LoadQueueItem> regionGroups) -->

 LoadIncrementalHFiles.tryAtomicRegionLoad(final HConnection conn,final TableName tableName, final byte[] first, Collection<LoadQueueItem> lqis) -->

 ProtobufUtil.bulkLoadHFile(final ClientService.BlockingInterface client,final List<Pair<byte[], String>> familyPaths,final byte[] regionName, boolean assignSeqNum) -->
 


其中ProtobufUtil.bulkLoadHFile()代码如下:
/**
   * A helper to bulk load a list of HFiles using client protocol.
   *
   * @param client
   * @param familyPaths
   * @param regionName
   * @param assignSeqNum
   * @return true if all are loaded
   * @throws IOException
   */
  public static boolean bulkLoadHFile(final ClientService.BlockingInterface client,
      final List<Pair<byte[], String>> familyPaths,
      final byte[] regionName, boolean assignSeqNum) throws IOException {
    BulkLoadHFileRequest request =
      RequestConverter.buildBulkLoadHFileRequest(familyPaths, regionName, assignSeqNum);
    try {
      BulkLoadHFileResponse response =
        client.bulkLoadHFile(null, request);
      return response.getLoaded();
    } catch (ServiceException se) {
      throw getRemoteException(se);
    }
  }

其中client是HRegionServer实例。
之后调用过程为:
HRegionServer --> HRegion.bulkLoadHFiles(...) --> HStore.bulkLoadHFile(...) --> HRegionFileSystem.bulkLoadStoreFile(...)

在HRegionFileSystem.bulkLoadStoreFile(...)这个方法判断源路径、目标路径是否同一个文件系统:
 /**
   * Bulk load: Add a specified store file to the specified family.
   * If the source file is on the same different file-system is moved from the
   * source location to the destination location, otherwise is copied over.
   *
   * @param familyName Family that will gain the file
   * @param srcPath {@link Path} to the file to import
   * @param seqNum Bulk Load sequence number
   * @return The destination {@link Path} of the bulk loaded file
   * @throws IOException
   */
  Path bulkLoadStoreFile(final String familyName, Path srcPath, long seqNum)
      throws IOException {
    // Copy the file if it's on another filesystem
    FileSystem srcFs = srcPath.getFileSystem(conf);
    FileSystem desFs = fs instanceof HFileSystem ? ((HFileSystem)fs).getBackingFs() : fs;

    // We can't compare FileSystem instances as equals() includes UGI instance
    // as part of the comparison and won't work when doing SecureBulkLoad
    // TODO deal with viewFS
    if (!srcFs.getUri().equals(desFs.getUri())) {
      LOG.info("Bulk-load file " + srcPath + " is on different filesystem than " +
          "the destination store. Copying file over to destination filesystem.");
      Path tmpPath = createTempName();
     //不是同一文件系统,则拷贝
      FileUtil.copy(srcFs, srcPath, fs, tmpPath, false, conf);
      LOG.info("Copied " + srcPath + " to temporary path on destination filesystem: " + tmpPath);
      srcPath = tmpPath;
    }

    return commitStoreFile(familyName, srcPath, seqNum, true);
  }


如果不是一个文件系统,那么就调用:
FileUtil.copy(...)

进行拷贝。

最终进行move操作的其实是一个rename操作:
 /**
   * Move the file from a build/temp location to the main family store directory.
   * @param familyName Family that will gain the file
   * @param buildPath {@link Path} to the file to commit.
   * @param seqNum Sequence Number to append to the file name (less then 0 if no sequence number)
   * @param generateNewName False if you want to keep the buildPath name
   * @return The new {@link Path} of the committed file
   * @throws IOException
   */
  private Path commitStoreFile(final String familyName, final Path buildPath,
      final long seqNum, final boolean generateNewName) throws IOException {
    Path storeDir = getStoreDir(familyName);
    if(!fs.exists(storeDir) && !createDir(storeDir))
      throw new IOException("Failed creating " + storeDir);
    
    String name = buildPath.getName();
    if (generateNewName) {
      name = generateUniqueName((seqNum < 0) ? null : "_SeqId_" + seqNum + "_");
    }
    Path dstPath = new Path(storeDir, name);
    if (!fs.exists(buildPath)) {
      throw new FileNotFoundException(buildPath.toString());
    }
    LOG.debug("Committing store file " + buildPath + " as " + dstPath);
    // buildPath exists, therefore not doing an exists() check.
    //在这里进行rename
    if (!rename(buildPath, dstPath)) {
      throw new IOException("Failed rename of " + buildPath + " to " + dstPath);
    }
    return dstPath;
  }


rename函数如下:
/**
   * Renames a directory. Assumes the user has already checked for this directory existence.
   * @param srcpath
   * @param dstPath
   * @return true if rename is successful.
   * @throws IOException
   */
  boolean rename(Path srcpath, Path dstPath) throws IOException {
    IOException lastIOE = null;
    int i = 0;
    do {
      try {
        return fs.rename(srcpath, dstPath);
      } catch (IOException ioe) {
        lastIOE = ioe;
        if (!fs.exists(srcpath) && fs.exists(dstPath)) return true; // successful move
        // dir is not there, retry after some time.
        sleepBeforeRetry("Rename Directory", i+1);
      }
    } while (++i <= hdfsClientRetriesNumber);
    throw new IOException("Exception in rename", lastIOE);
  }




分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics