hbase region split 源码分析

转载自:http://blackproof.iteye.com/blog/2037159

HBase region split源码分析

一、流程概述

1.HBaseAdmin 发起 hbase split

2.HRegionServer 确定分割点 region split point

3.CompactSplitThread和SplitRequest 进行region分割

  3.1SplitTransaction st.prepare()初始化两个子region

  3.2splitTransaction execute执行分割

        3.2.1两个子region DaughterOpener线程 start

        3.2.2若region 需要compact,进行compact路程

        3.2.3HRegionServer添加子region到meta表,加入到RegionServer里

   3.3修改zk节点状态,等待split结束

 

二 、hbase region split UML图

 

 

 

三、详细分析

1.HBaseAdmin 发起 hbase split

 

Java代码   收藏代码
  1. public void split(final byte [] tableNameOrRegionName,  
  2.     final byte [] splitPoint) throws IOException, InterruptedException {  
  3.   CatalogTracker ct = getCatalogTracker();  
  4.   try {  
  5.     Pair<HRegionInfo, ServerName> regionServerPair  
  6.       = getRegion(tableNameOrRegionName, ct);//获得HRI,若是但region  
  7.     if (regionServerPair != null) {  
  8.       if (regionServerPair.getSecond() == null) {  
  9.           throw new NoServerForRegionException(Bytes.toStringBinary(tableNameOrRegionName));  
  10.       } else {  
  11.         //split region 重点分析方法  
  12.         split(regionServerPair.getSecond(), regionServerPair.getFirst(), splitPoint);  
  13.       }  
  14.     } else {  
  15.       //table split流程  
  16.       final String tableName = tableNameString(tableNameOrRegionName, ct);  
  17.       List<Pair<HRegionInfo, ServerName>> pairs =  
  18.         MetaReader.getTableRegionsAndLocations(ct,  
  19.             tableName);  
  20.       for (Pair<HRegionInfo, ServerName> pair: pairs) {  
  21.         // May not be a server for a particular row  
  22.         if (pair.getSecond() == nullcontinue;  
  23.         HRegionInfo r = pair.getFirst();  
  24.         // check for parents  
  25.         if (r.isSplitParent()) continue;  
  26.         // if a split point given, only split that particular region  
  27.         if (splitPoint != null && !r.containsRow(splitPoint)) continue;  
  28.         // call out to region server to do split now  
  29.         split(pair.getSecond(), pair.getFirst(), splitPoint);  
  30.       }  
  31.     }  
  32.   } finally {  
  33.     cleanupCatalogTracker(ct);  
  34.   }  
  35. }  
 

 

2.HRegionServer 确定分割点 region split point

 

Java代码   收藏代码
  1.   @Override  
  2.   public void splitRegion(HRegionInfo regionInfo, byte[] splitPoint)  
  3.       throws NotServingRegionException, IOException {  
  4.     checkOpen();//检查server和hdfs是否可用  
  5.     HRegion region = getRegion(regionInfo.getRegionName());//根据HRI获取region  
  6.     region.flushcache();//flush cache 有几种情况不进行flush  
  7.      //the cache is empte | the region is closed.| a flush is already in progress | writes are disabled  
  8.     region.forceSplit(splitPoint);//设置split point  
  9.     compactSplitThread.requestSplit(region, region.checkSplit());//获取split point,进行split  
  10.   }  
   获得split point详细过程,获取最适合的store-hbase现在就是取最大的,获取store的midkey作为splitpoint
Java代码   收藏代码
  1. protected byte[] getSplitPoint() {  
  2.     byte[] explicitSplitPoint = this.region.getExplicitSplitPoint();  
  3.     if (explicitSplitPoint != null) {  
  4.       return explicitSplitPoint;  
  5.     }  
  6.     Map<byte[], Store> stores = region.getStores();  
  7.   
  8.     byte[] splitPointFromLargestStore = null;  
  9.     long largestStoreSize = 0;  
  10.     for (Store s : stores.values()) {  
  11.       byte[] splitPoint = s.getSplitPoint();  
  12.       long storeSize = s.getSize();  
  13.       if (splitPoint != null && largestStoreSize < storeSize) {//获得最大store  
  14.         splitPointFromLargestStore = splitPoint;  
  15.         largestStoreSize = storeSize;  
  16.       }  
  17.     }  
  18.       
  19.     return splitPointFromLargestStore;  
  20.   }  

 

3.CompactSplitThread和SplitRequest 进行region分割

这里是split中较为复杂的过程

Java代码   收藏代码
  1. public void run() {  
  2.     if (this.server.isStopping() || this.server.isStopped()) {  
  3.       LOG.debug("Skipping split because server is stopping=" +  
  4.         this.server.isStopping() + " or stopped=" + this.server.isStopped());  
  5.       return;  
  6.     }  
  7.     try {  
  8.       final long startTime = System.currentTimeMillis();  
  9.       SplitTransaction st = new SplitTransaction(parent, midKey);  
  10.       // If prepare does not return true, for some reason -- logged inside in  
  11.       // the prepare call -- we are not ready to split just now. Just return.  
  12.       //<strong> 3.1SplitTransaction st.prepare()初始化两个子region</strong>  
  13.       if (!st.prepare()) return;  
  14.       try {  
  15.         st.execute(this.server, this.server);//<strong>3.2splitTransaction execute执行分割</strong>  
  16.         this.server.getMetrics().incrementSplitSuccessCount();  
  17.       } catch (Exception e) {  
  18. 。。。。。。。。。。。。  

 

  3.2splitTransaction execute执行分割

Java代码   收藏代码
  1. public PairOfSameType<HRegion> execute(final Server server,  
  2.     final RegionServerServices services)  
  3. throws IOException {  
  4.   PairOfSameType<HRegion> regions = createDaughters(server, services);  
  5. //创建split临时目录,改变region zk状态,关闭region,停止所有store服务  
  6. //创建daughter目录,将region storefile放入目录中  
  7. //创建子region A、B,在zk上注册,并且设置原HRI下线  
  8.   openDaughters(server, services, regions.getFirst(), regions.getSecond());  
  9.   transitionZKNode(server, services, regions.getFirst(), regions.getSecond());  
  10.   return regions;  
  11. }  

        3.2.1两个子region DaughterOpener线程 start

Java代码   收藏代码
  1. final RegionServerServices services, HRegion a, HRegion b)  
  2.    throws IOException {  
  3.  boolean stopped = server != null && server.isStopped();  
  4.  boolean stopping = services != null && services.isStopping();  
  5.  // TODO: Is this check needed here?  
  6.  if (stopped || stopping) {  
  7.    LOG.info("Not opening daughters " +  
  8.        b.getRegionInfo().getRegionNameAsString() +  
  9.        " and " +  
  10.        a.getRegionInfo().getRegionNameAsString() +  
  11.        " because stopping=" + stopping + ", stopped=" + stopped);  
  12.  } else {  
  13.    // Open daughters in parallel.创建两个字region打开操作类  
  14.    DaughterOpener aOpener = new DaughterOpener(server, a);  
  15.    DaughterOpener bOpener = new DaughterOpener(server, b);  
  16.    aOpener.start();  
  17.    bOpener.start();  
  18.    try {  
  19.      aOpener.join();  
  20.      bOpener.join();  
  21.    } catch (InterruptedException e) {  
  22.      Thread.currentThread().interrupt();  
  23.      throw new IOException("Interrupted " + e.getMessage());  
  24.    }  
  25.    if (aOpener.getException() != null) {  
  26.      throw new IOException("Failed " +  
  27.        aOpener.getName(), aOpener.getException());  
  28.    }  
  29.    if (bOpener.getException() != null) {  
  30.      throw new IOException("Failed " +  
  31.        bOpener.getName(), bOpener.getException());  
  32.    }  
  33.    if (services != null) {  
  34.      try {  
  35.        // add 2nd daughter first (see HBASE-4335)  
  36.        services.postOpenDeployTasks(b, server.getCatalogTracker(), true);  
  37.        // Should add it to OnlineRegions  
  38.        services.addToOnlineRegions(b);  
  39.        services.postOpenDeployTasks(a, server.getCatalogTracker(), true);  
  40.        services.addToOnlineRegions(a);  
  41.      } catch (KeeperException ke) {  
  42.        throw new IOException(ke);  
  43.      }  
  44.    }  
  45.  }  

 调用HRegion 打开方法openHRegion

  

Java代码   收藏代码
  1.   protected HRegion openHRegion(final CancelableProgressable reporter)  
  2.   throws IOException {  
  3.     checkCompressionCodecs();  
  4.   
  5.     long seqid = initialize(reporter);  
  6. //初始化region,  
  7. //1.checkRegionInfoOnFilesystem将HRegionInfo写入文件  
  8. //2.cleanupTempDir 清空老region临时目录  
  9. //3.初始化HRegion store,加载hfile  
  10. //4.获得recover.edit文件,找到对应的store,将读取的keyvalue输出到store,恢复hregion  
  11.     if (this.log != null) {  
  12.       this.log.setSequenceNumber(seqid);  
  13.     }  
  14.     return this;  
  15.   }  

        3.2.2若region 需要compact,进行compact过程

   compact过程有点复杂,过程如下:

1.将所有storefile放入compact候选者

2.交给coprocessor做处理,选择compact storefile

3.若coprocessor没有做处理,则采用系统算法选择

  3.1必须要进行compact的文件,文件大小大于compact最大值并且没有其他被引用

  3.2必须要进行compact文件小于compact文件最小数

  3.3 isMajorCompaction判断是否需要major compact

          3.3.1当ttl大于storefile中最大文件compact time,则不需要

          3.3.2 以上反之,需要

          3.3.3 最后一次major compaction时间大于majorCompactionTime,需要

  3.4 当compact文件大于compact文件最大数,且需要major compaction活强制major compaction,则进行major compaction

  3.5或则进行minor compact,他两个的区别在于一个compact文件数是所有,一个compact文件数不大于maxcompactfile配置

Java代码   收藏代码
  1. public CompactionRequest requestCompaction(int priority) throws IOException {  
  2.     // don't even select for compaction if writes are disabled  
  3.     if (!this.region.areWritesEnabled()) {  
  4.       return null;  
  5.     }  
  6.   
  7.     CompactionRequest ret = null;  
  8.     this.lock.readLock().lock();  
  9.     try {  
  10.       synchronized (filesCompacting) {  
  11.         // candidates = all storefiles not already in compaction queue  
  12.         List<StoreFile> candidates = Lists.newArrayList(storefiles);  
  13.         if (!filesCompacting.isEmpty()) {  
  14.           // exclude all files older than the newest file we're currently  
  15.           // compacting. this allows us to preserve contiguity (HBASE-2856)  
  16.           StoreFile last = filesCompacting.get(filesCompacting.size() - 1);  
  17.           int idx = candidates.indexOf(last);  
  18.           Preconditions.checkArgument(idx != -1);  
  19.           candidates.subList(0, idx + 1).clear();  
  20.         }  
  21.   
  22.         boolean override = false;  
  23.         if (region.getCoprocessorHost() != null) {  
  24.           override = region.getCoprocessorHost().preCompactSelection(  
  25.               this, candidates);  
  26.         }  
  27.         CompactSelection filesToCompact;  
  28.         if (override) {  
  29.           // coprocessor is overriding normal file selection  
  30.           filesToCompact = new CompactSelection(conf, candidates);  
  31.         } else {  
  32.           filesToCompact = compactSelection(candidates, priority);  
  33.         }  
  34.   
  35.         if (region.getCoprocessorHost() != null) {  
  36.           region.getCoprocessorHost().postCompactSelection(this,  
  37.               ImmutableList.copyOf(filesToCompact.getFilesToCompact()));  
  38.         }  
  39.   
  40.         // no files to compact  
  41.         if (filesToCompact.getFilesToCompact().isEmpty()) {  
  42.           return null;  
  43.         }  
  44.   
  45.         // basic sanity check: do not try to compact the same StoreFile twice.  
  46.         if (!Collections.disjoint(filesCompacting, filesToCompact.getFilesToCompact())) {  
  47.           // TODO: change this from an IAE to LOG.error after sufficient testing  
  48.           Preconditions.checkArgument(false"%s overlaps with %s",  
  49.               filesToCompact, filesCompacting);  
  50.         }  
  51.         filesCompacting.addAll(filesToCompact.getFilesToCompact());  
  52.         Collections.sort(filesCompacting, StoreFile.Comparators.FLUSH_TIME);  
  53.   
  54.         // major compaction iff all StoreFiles are included  
  55.         boolean isMajor = (filesToCompact.getFilesToCompact().size() == this.storefiles.size());  
  56.         if (isMajor) {  
  57.           // since we're enqueuing a major, update the compaction wait interval  
  58.           this.forceMajor = false;  
  59.         }  
  60.   
  61.         // everything went better than expected. create a compaction request  
  62.         int pri = getCompactPriority(priority);  
  63.         ret = new CompactionRequest(region, this, filesToCompact, isMajor, pri);  
  64.       }  
  65.     } finally {  
  66.       this.lock.readLock().unlock();  
  67.     }  
  68.     if (ret != null) {  
  69.       CompactionRequest.preRequest(ret);  
  70.     }  
  71.     return ret;  
  72.   }  

 

        3.2.3HRegionServer添加子region到meta表,加入到RegionServer里

       更新meta表

Java代码   收藏代码
  1. // If daughter of a split, update whole row, not just location.更新meta表 loaction和rowkey  
  2. MetaEditor.addDaughter(ct, r.getRegionInfo(),  
  3.   this.serverNameFromMasterPOV);  

      加入regionserver

Java代码   收藏代码
  1. public void addToOnlineRegions(HRegion region) {  
  2.    this.onlineRegions.put(region.getRegionInfo().getEncodedName(), region);  
  3.  }  

 

   3.3修改zk节点状态,等待split结束

 

Java代码   收藏代码
  1. /* package */void transitionZKNode(final Server server,  
  2.       final RegionServerServices services, HRegion a, HRegion b)  
  3.       throws IOException {  
  4.     // Tell master about split by updating zk.  If we fail, abort.  
  5.     if (server != null && server.getZooKeeper() != null) {  
  6.       try {  
  7.         this.znodeVersion = transitionNodeSplit(server.getZooKeeper(),  
  8.           parent.getRegionInfo(), a.getRegionInfo(), b.getRegionInfo(),  
  9.           server.getServerName(), this.znodeVersion);  
  10.   
  11.         int spins = 0;  
  12.         // Now wait for the master to process the split. We know it's done  
  13.         // when the znode is deleted. The reason we keep tickling the znode is  
  14.         // that it's possible for the master to miss an event.  
  15.         do {  
  16.           if (spins % 10 == 0) {  
  17.             LOG.debug("Still waiting on the master to process the split for " +  
  18.                 this.parent.getRegionInfo().getEncodedName());  
  19.           }  
  20.           Thread.sleep(100);  
  21.           // When this returns -1 it means the znode doesn't exist  
  22.           this.znodeVersion = tickleNodeSplit(server.getZooKeeper(),  
  23.             parent.getRegionInfo(), a.getRegionInfo(), b.getRegionInfo(),  
  24.             server.getServerName(), this.znodeVersion);  
  25.           spins++;  
  26.         } while (this.znodeVersion != -1 && !server.isStopped()  
  27.             && !services.isStopping());  

 

结束了,有时间再看看compact过程,其实在split中已经包含compact的过程,不知道是不是所有的compact流程都一样


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值