在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]);