hbase assign region 深入分析

转载自:http://ronxin999.blog.163.com/blog/static/42217920201311363453387/

参数属性

hbase.master.startup.retainassign master :
      启动时的region分配算法选择,默认是即根据region在meta表中是归属那个RS,还是分配到 
      原来的RS,不存在的,则随机选择,否则随机生成
hbase.assignment.maximum.attempts  master分配region尝试的次数,默认为10次
hbase.master.assignment.timeoutmonitor.period 10000,即10s

两个状态:

   zkNode

      zookeeper 上的path: hbase/unassigned/regionName,
      zkNode是由master创建(开始分配region的时候)。和删除(region在RS成功上线)。

  RegionState

    Master的内存状态

MASTER

1 Assignment在分配region时,首先设置该region在zk上的状态为M_ZK_REGION_OFFLINE,
  RegionState 对应的状态为OFFLINE,有一个需要注意的是  因为master发现region在PENDING_OPEN/OPENING很长时间时,会强制设置zknode为 M_ZK_REGION_OFFLINE ,
 但同时region server可能已经打开了相关的region,并设置zknode为RS_ZK_REGION_OPENED,所以如果因为 opengregion的时间过长,而导致reassign region时,而且region state为PENDING_OPEN/OPENING时,只需要直接更新region state为PENDING_OPEN即可。不需要设置zknode为M_ZK_REGION_OFFLINE

2 创建分配region的regionPlan,创建前需要先检查是否有Draining Server和Dead Server,如果有,需要从server list中remove掉。如果是force assign/老的regionPlan不存在/老的regionPlan的目标RS是Draining的 ,则需要重新创建一个regionPlan,目标rs则随机选择一个。
3 得到regionPlan后,master更新regionState状态为PENDING_OPEN。
4 发送RPC请求RS 开始做openRegion的工作。

注意:master是通过StartupBulkAssigner来并发分配多个RS上的regions的。为每个RS创建一个
         SingleServerBulkAssigner runnable交给线程池来执行。同样每个RS对多个regions也是并行加载的
         是通过ExecutorService 线程池来执行每个OpenRegionHandler的。

TimeoutMonitor

主要是监控regionsInTransition中的RegionState是否超时,如果超时,则重新分配region的上线工作。

TimerUpdater

timeoutMonitor负责监听在rit状态中的region 分配是否超时,如果有一个region分配到多个region,但是每个RS加载region是同步的,这样可能一个region在加载的过程中,其他的region有可能被timeoutMonitor发现超时,所以在master在发现RS成功上线一个region后,会通过TimeUpdater来更新该RS上其它待加载的region的
时间戳。

RegionServer

1 先检查该region在RS是否正在事物中。
2 检查该region是否在RS的上线region列表,如果存在,还需要检查该region在meta表中的标记的是哪个RS,如果meta表中标记的RS是当前要打开该region的RS,则直接返回region已经在线的状态ALREADY_OPENED给Master。否则需要把该region从online region列表中remove掉。
3 添加region的name到regionsInTransitionInRS中
4 根据region的类型创建不同的runnable交给线程池执行,根据region的类型,有如下三种线程:
    Root Region :OpenRootHandler
    Meta Region :OpenMetaHandler
    用户Region    :OpenRegionHandler
5 RS返回OPENED状态给master,返回OPENED只是告诉Master,RS已经接受了该region,并正在打开。
   但并不说明已经上线了给region。如果返回ALREADY_OPENED,则说明已经上线,

OpenRegionHandler

1 handler 首先修改zknode的状态从M_ZK_REGION_OFFLINE到RS_ZK_REGION_OPENING状态,这个需要
   校验版本和原始状态。
2 开始打开region。打开region的操作比较耗时,详细过程为

    2.1  持久化region信息到disk,目录为region path下的.regioninfo文件,方便用来recover 系统META表。
       如果已经存在,则不需要写。写的时候也是先写到region的.tmp/.regioninfo目录下,写成功后,
       在重命名 回来到region/.regioninfo。这样写是因为怕在写的时候关闭前,datanode当机,导致后面
       的region恢复会失败。因为写文件需要在namenode上注册获得文件的租约。

    2.2  清理该region目录下的.tmp文件。
    2.3 初始化所有的store。
    2.4 store初始化成功后,开始恢复HLog的增量数据。hlog修改日志数据保存在
          region-path/recovered.edits目录下以.temp结尾的文件。在读这些文件的时候,会过滤掉以数字
          开头但不 是以.temp结尾的文件,这些文件时错误或者无效的文件。
    2.5 读日志文件内容到memstore,因为每个store都对应一个LogSeqNum,所以恢复时以最小的为准,
         每个hlog文件保护多个HLog.Entry(HLogKey,WALEdit的键值对)。每个HLogKey都记录
         一个 LogSeqNum,在恢复时需要跳过比store最小的LogSeqNum。因为这些已经flush到HFile了。
         具体恢复到哪个store,是根据WALEdit中的KeyValue的family来决定的。在不断恢复WALEdit的
         过程中,也会检查memstore的大小,如果超过了flush的临界值,则flush。
    2.6 清理split目录region/.splits目录
    2.7 删除.merges目录
    2.8 创建split策略。

3  打开region完成后,需要判断是否成功,如果失败,则更新zknode的状态
    为   RS_ZK_REGION_FAILED_OPEN后,返回,RS打开region这个任务也就结束了。
4  如果打开成功,则开始上线region,主要涉及到更新zknode的状态,把region的位置添加到META表。
    详细过程如下:
    
   4.1 刷新zknode的状态,即重新更新为RS_ZK_REGION_OPENING,为什么说重新更新,是因为到这一步,
         zknode的状态同样是RS_ZK_REGION_OPENING。好多人可能会有疑问,为什么要做这一步呢。原因是
         防止master那边认为该region的分配认为超时。注意每更新zknode的状态,都会重新时间戳,
        这样Master端的zookeeper watch收到更新事件时,也会更新对应的region state的时间戳。
        这样就不会被TimeoutMonitor认为超时了。
   4.2 在上面flush成功后,进入到更新META表,这里可能要多花点时间,因为META表可能还没有上线,
         或者 META表的region所在的region刚好crashed了,这都需要等待META表恢复。所有这里会取用一个
         单独的线程:PostOpenDeployTasksThread来更新META表,而主线程定期更新zknode的状态
         为RS_ZK_REGION_OPENING,同样是为了防止不让master认为该region在分配过程中超时。
        PostOpenDeployTasksThread的任务从名字就可以看出,它是来负责往META表添加一条region的
       位置记录。核心的代码如下:   


 
    
    
   final boolean daughter)
  throws KeeperException, IOException {
    checkOpen();
    LOG.info("Post open deploy tasks for region=" + r.getRegionNameAsString() +
      ", daughter=" + daughter);
    // Do checks to see if we need to compact (references or too many files)
    for (Store s : r.getStores().values()) {
       //如果是分裂的,则会进行compact动作。
      if (s.hasReferences() || s.needsCompaction()) {
        getCompactionRequester().requestCompaction(r, s, "Opening Region");
      }
    }
    // Update ZK, ROOT or META
    if (r.getRegionInfo().isRootRegion()) {
      RootLocationEditor.setRootLocation(getZooKeeper(),
       this.serverNameFromMasterPOV);
    } else if (r.getRegionInfo().isMetaRegion()) {
      MetaEditor.updateMetaLocation(ct, r.getRegionInfo(),
        this.serverNameFromMasterPOV);
    } else {
      if (daughter) {
        // If daughter of a split, update whole row, not just location.
        MetaEditor.addDaughter(ct, r.getRegionInfo(),
          this.serverNameFromMasterPOV);
      } else {
        MetaEditor.updateRegionLocation(ct, r.getRegionInfo(),
          this.serverNameFromMasterPOV);
      }
    }
    LOG.info("Done with post open deploy task for region=" +
      r.getRegionNameAsString() + ", daughter=" + daughter);
  }


  如果你看过region分裂的代码,应该有印象,因为在region分裂完后,也会执行这个方法来compact store
  主线程给PostOpenDeployTasksThread线程的时间为timeout = assignmentTimeout的10倍,assignmentTimeout
  可以通过参数hbase.master.assignment.timeoutmonitor.period来设置,默认为10s,主线程每assignmentTimeout/3的时间会更新zknode的状态,如果在timeout 的时间内PostOpenDeployTasksThread
 线程还没有完成,则中断该线程,然后join,等待线程结束。代码入下:

 
 
   
boolean updateMeta(final HRegion r) {
    if (this.server.isStopped() || this.rsServices.isStopping()) {
      return false;
    }
    // Object we do wait/notify on. Make it boolean. If set, we're done.
    // Else, wait.
    final AtomicBoolean signaller = new AtomicBoolean(false);
    PostOpenDeployTasksThread t = new PostOpenDeployTasksThread(r,
      this.server, this.rsServices, signaller);
    t.start();
    int assignmentTimeout = this.server.getConfiguration().
      getInt("hbase.master.assignment.timeoutmonitor.period", 10000);
    // Total timeout for meta edit. If we fail adding the edit then close out
    // the region and let it be assigned elsewhere.
    long timeout = assignmentTimeout * 10;
    long now = System.currentTimeMillis();
    long endTime = now + timeout;
    // Let our period at which we update OPENING state to be be 1/3rd of the
    // regions-in-transition timeout period.
    long period = Math.max(1, assignmentTimeout/ 3);
    long lastUpdate = now;
    boolean tickleOpening = true;
    while (!signaller.get() && t.isAlive() && !this.server.isStopped() &&
        !this.rsServices.isStopping() && (endTime > now)) {
      long elapsed = now - lastUpdate;
      if (elapsed > period) {
        // Only tickle OPENING if postOpenDeployTasks is taking some time.
        lastUpdate = now;
        tickleOpening = tickleOpening("post_open_deploy");
      }
      synchronized (signaller) {
        try {
          signaller.wait(period);
        } catch (InterruptedException e) {
          // Go to the loop check.
        }
      }
      now = System.currentTimeMillis();
    }
    // Is thread still alive? We may have left above loop because server is
    // stopping or we timed out the edit. Is so, interrupt it.
    if (t.isAlive()) {
      if (!signaller.get()) {
        // Thread still running; interrupt
        LOG.debug("Interrupting thread " + t);
        t.interrupt();
      }
      try {
        t.join();
      } catch (InterruptedException ie) {
        LOG.warn("Interrupted joining " +
          r.getRegionInfo().getRegionNameAsString(), ie);
        Thread.currentThread().interrupt();
      }
    }
    // Was there an exception opening the region? This should trigger on
    // InterruptedException too. If so, we failed. Even if tickle opening fails
    // then it is a failure.
    return ((!Thread.interrupted() && t.getException() == null) && tickleOpening);
  }

   
5 如果更新meta表成功,则更新zknode的状态为RS_ZK_REGION_OPENED。把该region添加到在线列表 online,并从regionsInTransitionInRS中remove掉该region的记录。如果更新meta失败,则需要把打开的region close掉。同时zknode的状态更新为RS_ZK_REGION_FAILED_OPEN。到此,RS上线region的过程就算结束了。

你可能感兴趣的:(编程及调试)