最近重新回到熟悉的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的一致性也还是造成挑战。
