在HBase入庫日志中發現有一個表入庫失敗,檢查HBase服務端后發現該表的meta信息丟失了:
而HDFS上的region還在:
而HBCK工具不支持HBase2.0版本,只好自己寫一個修復工具。網上可以搜到前輩們自己編寫的一些工具,比如這一篇寫的就比較詳細 https://blog.csdn.net/xyzkenan/article/details/103476160
我們引用一下,再對個別地方略微講解。
看一下'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內容相同 |
接下來就開始動手coding:
獲取'hbase:mata'中的Region信息
1 public Set<String> getMetaRegions(Configuration conf, String tableName) throws Exception { 2 3 Connection conn = ConnectionFactory.createConnection(conf); 4 Table table = conn.getTable(TableName.valueOf(TABLE)); 5 6 PrefixFilter filter = new PrefixFilter(Bytes.toBytes(tableName + ",")); 7 8 Scan scan = new Scan(); 9 scan.setFilter(filter); 10 11 Set<String> metaRegions = new HashSet<>(); 12 13 Iterator<Result> iterator = table.getScanner(scan).iterator(); 14 while (iterator.hasNext()) { 15 Result result = iterator.next(); 16 metaRegions.add(Bytes.toString(result.getRow())); 17 } 18 19 conn.close(); 20 21 return metaRegions; 22 }
讀取.regioninfo中的Region信息
1 public Map<String, RegionInfo> getHdfsRegions(Configuration conf, String tablePath) throws Exception { 2 3 FileSystem fs = FileSystem.get(conf); 4 Path path = new Path(hdfsRootDir + "/data/default/" + tablePath + "/"); 5 6 Map<String, RegionInfo> hdfsRegions = new HashMap<>(); 7 8 FileStatus[] list = fs.listStatus(path); 9 for (FileStatus status : list) { 10 if (!status.isDirectory()) { 11 continue; 12 } 13 14 boolean isRegion = false; 15 FileStatus[] regions = fs.listStatus(status.getPath()); 16 for (FileStatus regionStatus : regions) { 17 if (regionStatus.toString().contains(REGION_INFO_FILE)) { 18 isRegion = true; 19 break; 20 } 21 } 22 23 if (!isRegion) { 24 continue; 25 } 26 27 RegionInfo hri = HRegionFileSystem.loadRegionInfoFileContent(fs, status.getPath()); 28 hdfsRegions.put(hri.getRegionNameAsString(), hri); 29 30 } 31 return hdfsRegions; 32 }
兩者進行對比取差集
1 Set<String> metaRegions = getMetaRegions(configuration, repairTableName); 2 3 Map<String, RegionInfo> hdfsRegions = getHdfsRegions(configuration, repairTableName); 4 5 Set<String> hdfsRegionNames = hdfsRegions.keySet(); 6 7 metaRegions.removeAll(hdfsRegionNames);
構造META信息並寫入HBase
1 ServerName[] regionServers = admin.getRegionServers().toArray(new ServerName[0]); 2 3 int rsLength = regionServers.length; 4 int i = 0; 5 for (String regionName : hdfsRegionNames) { 6 7 String sn = regionServers[i % rsLength].getServerName(); 8 String[] snSig = sn.split(","); 9 10 RegionInfo hri = hdfsRegions.get(regionName); 11 Put info = MetaTableAccessor.makePutFromRegionInfo(hri, EnvironmentEdgeManager.currentTime()); 12 info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(SN), Bytes.toBytes(sn)); 13 info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(SERVER), Bytes.toBytes(snSig[0] + ":" + snSig[1])); 14 info.addColumn(Bytes.toBytes(FAMILY), Bytes.toBytes(STATE), Bytes.toBytes("OPEN")); 15 16 table.put(info); 17 i++; 18 }
注意這里的各個region是輪流分配給各個regionServers,具體分配可能和meta信息丟失前不同,但是沒關系,一個regionServer管理多個region,映射關系可以改變的,重啟HBase服務后會生效,HBase內部也會平衡每個regionServer管理的region數量,也可在hbase shell中手動觸發平衡。
使用工具修復后,先驗證一下是否生成了新的meta信息:
確認無誤,接下來重啟所有HBase服務,重啟之后會自動生成'info:seqnumDuringOpen'以及'info:serverstartcode'
重啟之后,驗證修復好的表是否可以讀寫:
OK,至此基本大功告成,再把工具稍加封裝,融入我的組件包內,添加到REST API里,以后再遇到這種問題,發一個POST請求,瞬間修復。
最后還引用一下前輩封裝好的工具以供下載使用,https://github.com/darkphoenixs/hbase-meta-repair
自己使用時要略加修改,比如你的hbase.root.dir可能不同,此外,我的HBase服務端雖然是2.0,但是我的修復工具使用的是1.x的包,有些接口略有不同,RegionInfo對應的是HRegionInfo,此外,1.x獲取RegionServer使用的是
ServerName[] regionServers = admin.getClusterStatus.getServers().toArray(new ServerName[0]);