天天看點

HBase 2.0.0 META 資料修複工具

問題起因

必須先吐槽一下 Cloudera 6.x 和 Hbase 2.0 太坑了!

不久前生産上的一套Hbase叢集出現著名的RIT(Regions in Transition)問題。

檢視hbase web ui

HBase 2.0.0 META 資料修複工具

于是通過hbck指令檢視一下叢集狀态,果然好多inconsistency

...
 ERROR: Region { meta => XXX,XXX:,1573019231000.ff2aecaf28917792395c341d01e0b8cc., hdfs => hdfs://nameservice1/hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc, deployed => , replicaId => 0 } not deployed on any region server.
 ...
 ERROR: Found inconsistency in table XXX
 ...
 9 inconsistencies detected.
 Status: INCONSISTENT           

看到錯誤提示問題明顯了,這個Region在hdfs中有資料檔案但沒有依賴任何Region Server,原因可能region被原來的Region Server unassigned了,但是還沒有被assigned到一個新的Region Server上。

那麼嘗試用

hbase hbck -repair

hbase hbck -fixMeta -fixAssignments

來修複吧,于是就有了下面的提示,hbase2.0+以後hbck的所有修複功能全都不支援...

-----------------------------------------------------------------------
NOTE: As of HBase version 2.0, the hbck tool is significantly changed.
In general, all Read-Only options are supported and can be be used
safely. Most -fix/ -repair options are NOT supported. Please see usage
below for details on which options are not supported.
-----------------------------------------------------------------------

NOTE: Following options are NOT supported as of HBase version 2.0+.

  UNSUPPORTED Metadata Repair options: (expert features, use with caution!)
   -fix              Try to fix region assignments.  This is for backwards compatiblity
   -fixAssignments   Try to fix region assignments.  Replaces the old -fix
   -fixMeta          Try to fix meta problems.  This assumes HDFS region info is good.
   -fixHdfsHoles     Try to fix region holes in hdfs.
   ...
  UNSUPPORTED Metadata Repair shortcuts
   -repair           Shortcut for -fixAssignments -fixMeta -fixHdfsHoles -fixHdfsOrphans -fixHdfsOverlaps -fixVersionFile -sidelineBigOverlaps -fixReferenceFiles-fixHFileLinks
   -repairHoles      Shortcut for -fixAssignments -fixMeta -fixHdfsHoles
           

既然hbck不支援,覺得hbase總得有解決方案吧,科學上網後發現hbase2.0+提供了一個叫hbck2工具,不過得自己編譯麻煩了點。

克隆下來準備動手編譯發現不對,于是仔細看了一下hbck2的介紹,

最低支援版本2.0.3和2.1.1
HBase 2.0.0 META 資料修複工具
HBase 2.0.0 META 資料修複工具

WTF......這就是個黑洞啊,還有你就不能把支援的版本号字型放大點嗎!

修複方案

吐槽過後,還是得想解決辦法啊:

  1. 更新Hbase版本
    • 目前這種情況是根本無法更新的,存量資料怎麼辦,就算資料可以重入,目前使用的hbase是CDH版,Cloudera 6.x版本內建的hbase隻有2.0.0和2.1.0版本,還是黑洞。。。此方案行不通。
  2. 暴力删除hbase資料
    • 暴力删除資料,格式化hdfs,删除hbasemeta資料,删除zookeeper記錄,這和重新部署一套hbase差不多了,但是前提是資料可以重入或者允許清除,那以後怎麼辦,總不能一遇到問題就删庫吧,生産上面的資料一般都比較敏感根本不能删。。。此方案行不通。
  3. 寫個工具修複hbase
    • 看來隻能這樣了。。。

修複步驟

回到最初的錯誤提示,思考一下,如果Region下資料檔案在hdfs中存在,那是否可以通過.regioninfo檔案(hdfs存儲hbase region資訊的檔案)擷取Region資訊,同時讀取'hbase:meta'表中的Region資訊,進行對比取差集就是要修複的Region,然後将需要修複的Region資訊再寫入到'hbase:meta'中。

按照這個思路,先驗證一下hdfs

檢測一下hbase的block是否完整

hdfs fsck /hbase

Status: HEALTHY
 Number of data-nodes:  12
 Number of racks:               1
 Total dirs:                    4650
 Total symlinks:                0
...
The filesystem under path '/hbase' is HEALTHY           

檢查一下.regioninfo檔案是否完整

hadoop fs -ls /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc

Found 4 items
-rw-r--r--   3 hbase hbase         65 2019-10-26 18:29 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/.regioninfo
drwxr-xr-x   - hbase hbase          0 2019-11-26 09:37 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/.tmp
drwxr-xr-x   - hbase hbase          0 2019-11-26 13:59 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/0
drwxr-xr-x   - hbase hbase          0 2019-10-26 18:29 /hbase/data/default/XXX/ff2aecaf28917792395c341d01e0b8cc/recovered.edits
           

再看一下'hbase:meta'中的存儲結構:

列名 說明
info:state Region狀态
info:sn Region Server Node,由 server和serverstartcode組成,如slave1,16020,1557998852385
info:serverstartcode Region Server啟動Code,實質上就是Region Server啟動的時間戳
info:server Region Server 位址和端口,如slave1:16020
info:seqnumDuringOpen 表示Region線上時長的一個二進制串
info:regioninfo Region Info,和.regioninfo内容相同

OK,覺得這個方案可行,接下來就開始動手coding吧

擷取'hbase:mata'中的Region資訊

public Set<String> getMetaRegions(Configuration conf, String tableName) throws Exception {

        Connection conn = ConnectionFactory.createConnection(conf);
        Table table = conn.getTable(TableName.valueOf(TABLE));

        PrefixFilter filter = new PrefixFilter(Bytes.toBytes(tableName + ","));

        Scan scan = new Scan();
        scan.setFilter(filter);

        Set<String> metaRegions = new HashSet<>();

        Iterator<Result> iterator = table.getScanner(scan).iterator();
        while (iterator.hasNext()) {
            Result result = iterator.next();
            metaRegions.add(Bytes.toString(result.getRow()));
        }

        conn.close();

        return metaRegions;
    }           

讀取.regioninfo中的Region資訊

public Map<String, RegionInfo> getHdfsRegions(Configuration conf, String tablePath) throws Exception {

        FileSystem fs = FileSystem.get(conf);
        Path path = new Path(hdfsRootDir + "/data/default/" + tablePath + "/");

        Map<String, RegionInfo> hdfsRegions = new HashMap<>();

        FileStatus[] list = fs.listStatus(path);
        for (FileStatus status : list) {
            if (!status.isDirectory()) {
                continue;
            }

            boolean isRegion = false;
            FileStatus[] regions = fs.listStatus(status.getPath());
            for (FileStatus regionStatus : regions) {
                if (regionStatus.toString().contains(REGION_INFO_FILE)) {
                    isRegion = true;
                    break;
                }
            }

            if (!isRegion) {
                continue;
            }

            RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, status.getPath());
            hdfsRegions.put(hri.getRegionNameAsString(), hri);

        }
        return hdfsRegions;
    }           

兩者進行對比取差集

Set<String> metaRegions = getMetaRegions(configuration, repairTableName);

        Map<String, RegionInfo> hdfsRegions = getHdfsRegions(configuration, repairTableName);

        Set<String> hdfsRegionNames = hdfsRegions.keySet();

        metaRegions.removeAll(hdfsRegionNames);           

構造META資訊并寫入HBase

ServerName[] regionServers = admin.getRegionServers().toArray(new ServerName[0]);
        
        int rsLength = regionServers.length;
        int i = 0;
        for (String regionName : hdfsRegionNames) {

            String sn = regionServers[i % rsLength].getServerName();
            String[] snSig = sn.split(",");

            RegionInfo hri = hdfsRegions.get(regionName);
            Put info = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime());
            info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(SN), Bytes.toBytes(sn));
            info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(SERVER), Bytes.toBytes(snSig[0] + ":" + snSig[1]));
            info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(STATE), Bytes.toBytes("OPEN"));

            table.put(info);
            i++;
        }           
重新開機Region Server 和 Hbase Master,重新開機之後會自動生成'info:seqnumDuringOpen'以及'info:serverstartcode'

工具開發完成後,找了個環境驗證了一下,沒出什麼問題,接下來就部署到生産上試試了,反正hbase已經這個樣子,死馬當司馬懿吧。

先用了個region不多的表試驗,發現可以呀,然後陸續把所有錯誤的表都修複一遍,重新開機hbase,接下來就是見證BUG的時刻:

...
0 inconsistencies detected.
Status: OK           

hbase修複完成 此處有掌聲

修複工具

本着開源精神,工具已上傳GitHub :

hbase-meta-repair
HBase 2.0.0 META 資料修複工具