1. 程式人生 > >Hbase表級別元資料一致性和hbck原理

Hbase表級別元資料一致性和hbck原理

最近重新回到熟悉的hbase領域,感慨還是很多。首先終於又可以沉下心來好好搞技術了,其次看到現在有衝勁有追求的年輕人就像看到原來的自己。大資料需要一代一代人傳承下去。

最近處於叢集管理方便以及資源合理利用的考慮我們上線了region group的patch,將原來在2.0裡面才合併的patch 加到了0.98版本中。初始使用的時候挺好,但是也遇到了一點問題——在做表group之間遷移的時候發現master頁面上的元資料資訊有誤。而實際的region分配卻沒問題

為啥會出現這種情況,就需要我們瞭解hbase關於表、region元資料如何管理的問題。

首先,大家知道的比較多的是zk中儲存的元資料資訊

一、ZK元資料

第一級目錄/hbase
第二級子目錄
/meta-region-server## meta表所在的rs位置,最初的bigtable論文中是有root表到meta表兩級的,hbase原來也有,後來是發現一個meta表能索引的region數量已經足夠用了,而多加一級root表多一次路由沒意義就捨棄掉了
/acl## 子節點儲存表以及namespace級別許可權控制,再下一級子節點儲存哪些user擁有什麼許可權
/backup-masters## 子節點儲存standby master的地址,埠,啟動時間
/table## 子節點儲存這個叢集所有的表資訊,無論是否enable
/draining## 儲存regionserver的臨時變化情況,一般是下線多個regionserver時使用
/region-in-transition## 儲存處於事物中的region(split/online/offline/compact等)
/running## hbase叢集是否正常執行
/table-lock## 鎖表資訊,在表發生變更時使用
/master## 叢集的master地址
/balancer## loaderbalancer是否被開啟
/namespace## 當前所有的namespace
/hbaseid## 叢集啟動時生成的唯一id
/online-snapshot## 線上的快照
/replication## hbase的replication配置,有rs和peers兩個元資料資訊
/groupInfo## 儲存的group資訊
/splitWAL## 用來構造一個region server的splitlog目錄
/recovering-regions## 儲存恢復中的regions
/rs## 當前所有線上的regionserver資訊

二、meta表元資料

meta表中儲存的就是所有region狀態的資訊

rowkey組成為——namespace:tablename,,timestamp.md5

列族為info,子列包括

server——region所屬regionserver位置

serverstartcode——server啟動的startcode,rs每次重啟之後就不是"自己"了,而是用startcode標識的一個rs,所以要重新分配region

regioninfo——region的ENCODED, STARTKEY, ENDKEY

三、HDFS目錄中元資料

/hbase/.tmp## 臨時目錄,一般是存放compact、split等操作過程中的臨時檔案
/hbase/WALs		## 儲存每個regionserver的WAL日誌,子目錄是每個rs的名稱
/hbase/archive## 儲存compact過程中不用的HFile,刪除表的資料也再,過期(5分鐘)會被刪除.可以用來恢復誤drop的表,快照也儲存在這
/hbase/corrupt## 錯誤檔案路徑
/hbase/data## 所有的表資料都在data下
/hbase/hbase.id## 當前叢集啟動的id,每次啟動都不同
/hbase/hbase.version## hbase版本號
/hbase/oldWALs## 過期的WAL日誌,等待被清除


四、Hmaster記憶體中的資料

這個部分的資料在任何地方都比較少介紹,但是其實是非常重要的!

infoServer——儲存web UI需要的相關資訊

ZookeeperWatcher——保持和zooKeeper連線

activeMasterManager——管理並存儲了當前active的master

regionServerTracker——追蹤regionserver

drainingServerTracker——追蹤drainning狀態regionserver

groupAdminServer——region group元資料資訊

tableNamespaceManager——namespace元資料資訊

五、HBck檢查過程

有了上面所說的元資料,大家可以注意到,同樣的一份資料在hbase中分別儲存在了4個不同的地方,資料就存在不一致的可能。那我們就從Hbase自帶的hbck角度來看看什麼樣的情況會被hbase認為是元資料異常,又是如何去做修復的?

這裡只分析核心檢查的部分,其餘檢查準備階段略過

// do the real work of hbck
    connect();

    try {
      // if corrupt file mode is on, first fix them since they may be opened later
      if (checkCorruptHFiles || sidelineCorruptHFiles) {
        LOG.info("Checking all hfiles for corruption");
        HFileCorruptionChecker hfcc = createHFileCorruptionChecker(sidelineCorruptHFiles);
        setHFileCorruptionChecker(hfcc); // so we can get result
        Collection<TableName> tables = getIncludedTables();
        Collection<Path> tableDirs = new ArrayList<Path>();
        Path rootdir = FSUtils.getRootDir(getConf());
        if (tables.size() > 0) {
          for (TableName t : tables) {
            tableDirs.add(FSUtils.getTableDir(rootdir, t));
          }
        } else {
          tableDirs = FSUtils.getTableDirs(FSUtils.getCurrentFileSystem(getConf()), rootdir);
        }
        hfcc.checkTables(tableDirs);
        hfcc.report(errors);
      }
      //到這一步先檢查HFile的資料格式是否正確,作為第一步做的檢查

      // check and fix table integrity, region consistency.
      int code = onlineHbck();
      //這裡呼叫了onlineHbck做線上檢查使用
      setRetCode(code);
      // If we have changed the HBase state it is better to run hbck again
      // to see if we haven't broken something else in the process.
      // We run it only once more because otherwise we can easily fall into
      // an infinite loop.
      if (shouldRerun()) {
        try {
          LOG.info("Sleeping " + sleepBeforeRerun + "ms before re-checking after fix...");
          Thread.sleep(sleepBeforeRerun);
        } catch (InterruptedException ie) {
          return this;
        }
        // Just report
        setFixAssignments(false);
        setFixMeta(false);
        setFixHdfsHoles(false);
        setFixHdfsOverlaps(false);
        setFixVersionFile(false);
        setFixTableOrphans(false);
        errors.resetErrors();
        code = onlineHbck();
        setRetCode(code);
      }
    } finally {
      IOUtils.cleanup(null, connection, meta, admin);
    }
    return this;
---------------------------------------------------------------------------------------------------------
/**
   * Contacts the master and prints out cluster-wide information
   * @return 0 on success, non-zero on failure
   */
  public int onlineHbck() throws IOException, KeeperException, InterruptedException, ServiceException {
    // print hbase server version
    errors.print("Version: " + status.getHBaseVersion());
    offlineHdfsIntegrityRepair();
    //這裡是對HBase表在hdfs路徑上的儲存路徑進行檢查,是否符合標準

    // turn the balancer off
    boolean oldBalancer = admin.setBalancerRunning(false, true);
    try {
      onlineConsistencyRepair();
    }
    finally {
      admin.setBalancerRunning(oldBalancer, false);
    }

    if (checkRegionBoundaries) {
      checkRegionBoundaries();
    }

    offlineReferenceFileRepair();

    checkAndFixTableLocks();

    // Check (and fix if requested) orphaned table ZNodes
    checkAndFixOrphanedTableZNodes();

    // Remove the hbck lock
    unlockHbck();

    // Print table summary
    printTableSummary(tablesInfo);
    return errors.summarize();
  }
--------------------------------------checkAndFixConsistency();-------------------------


private void checkRegionConsistencyConcurrently(
    final List<CheckRegionConsistencyWorkItem> workItems)
    throws IOException, KeeperException, InterruptedException {
    if (workItems.isEmpty()) {
      return;  // nothing to check
    }
    //workItems是具體去做修復的任務
    List<Future<Void>> workFutures = executor.invokeAll(workItems);
    for(Future<Void> f: workFutures) {
      try {
        f.get();
      } catch(ExecutionException e1) {
        LOG.warn("Could not check region consistency " , e1.getCause());
        if (e1.getCause() instanceof IOException) {
          throw (IOException)e1.getCause();
        } else if (e1.getCause() instanceof KeeperException) {
          throw (KeeperException)e1.getCause();
        } else if (e1.getCause() instanceof InterruptedException) {
          throw (InterruptedException)e1.getCause();
        } else {
          throw new IOException(e1.getCause());
        }
      }
    }
  }


六、思考
    目前看來hbase在處理元資料時資訊並不是集中儲存,對於一些操作失敗時會產生資料不一致的情況。提供了HBCK的方式進行修復,不過對於新的region group沒有做檢查以及修復元資料,待後續改進。
    另外,這種資料分散的方式對hbase的一致性也還是造成挑戰。