hbase split 源码分析之split策略

在工作中接触到split,于是查看了这块的源代码,先看到了split的策略,今天就说说这个吧,后续还会有split的其他源码分析和compact相关的源码分析。

看了很多其他人的博客,很多都是转发的,原创的也都没有注明是哪个版本。其实给很多读者造成混淆,我这里是基于Hbase-0.98.13  版本作为分析的,注意:不同版本的此部分源码很可能不一样。

在这个版本中使用的split策略是IncreasingToUpperBoundRegionSplitPolicy。确切来说他是0.94版本以后的策略。类为org/apache/hadoop/hbase/regionserver/IncreasingToUpperBoundRegionSplitPolicy.java 
    首先看一下 configureForRegion 方法,其中的initialSize 在以后会用到。这个方法其实主要目的也就是在初始化initialSize

@Override
  protected void configureForRegion(HRegion region) {
    super.configureForRegion(region);
    Configuration conf = getConf();
    //如果设置了hbase.increasing.policy.initial.size,则使用用户设置的
    this.initialSize = conf.getLong("hbase.increasing.policy.initial.size", -1);
    if (this.initialSize > 0) {
      return;
    }
    //如果没有设置,看hbase.hregion.memstore.flush.size有没有
    //如果设置了则initialSize=2*hbase.hregion.memstore.flush.size,
    //如果没有则使用默认的1024*1024*128L  (128M)
    HTableDescriptor desc = region.getTableDesc();
    if (desc != null) {
      this.initialSize = 2*desc.getMemStoreFlushSize();
    }
    if (this.initialSize <= 0) {
      this.initialSize = 2*conf.getLong(HConstants.HREGION_MEMSTORE_FLUSH_SIZE,
        HTableDescriptor.DEFAULT_MEMSTORE_FLUSH_SIZE);
    }
  }

如果配置默认,这个方法将initialSize 初始化为2*hbase.hregion.memstore.flush.size
再来看看其他的方法,有一个方法叫shouldSplit,顾名思义就是判断能不能split。

@Override
  protected boolean shouldSplit() {
    if (region.shouldForceSplit()) return true;
    boolean foundABigStore = false;
    //得到同张表的在线region个数
    // Get count of regions that have the same common table as this.region
    int tableRegionsCount = getCountOfCommonTableRegions();
    //得到分割的阀值
    // Get size to check
    long sizeToCheck = getSizeToCheck(tableRegionsCount);
    //检查每一个store,如果有不能split的则此次判断为false
    for (Store store : region.getStores().values()) {
      // If any of the stores is unable to split (eg they contain reference files)
      // then don‘t split
    	//如果当前region不能分割,则返回false
      if ((!store.canSplit())) {
        return false;
      }

      // Mark if any store is big enough
      long size = store.getSize();
      if (size > sizeToCheck) {
        LOG.debug("ShouldSplit because " + store.getColumnFamilyName() +
          " size=" + size + ", sizeToCheck=" + sizeToCheck +
          ", regionsWithCommonTable=" + tableRegionsCount);
        foundABigStore = true;
      }
    }

    return foundABigStore;
  }

其中long sizeToCheck = getSizeToCheck(tableRegionsCount);这句很重要,跟进去查看

protected long getSizeToCheck(final int tableRegionsCount) {
    // safety check for 100 to avoid numerical overflow in extreme cases
    return tableRegionsCount == 0 || tableRegionsCount > 100 ? getDesiredMaxFileSize():
      Math.min(getDesiredMaxFileSize(),
        this.initialSize * tableRegionsCount * tableRegionsCount * tableRegionsCount);
  }

这是一个三目运算,如果这个table中在线的region个数为0或则大于100,则使用getDesiredMaxFileSize()方法得到这个阀值,否则就使用getDesiredMaxFileSize()得到的阀值和initialSize * (tableRegionsCount的三次方)中小的那一个,在跟进去getDesiredMaxFileSize方法看看

long getDesiredMaxFileSize() {
    return desiredMaxFileSize;
  }

这个方法是ConstantSizeRegionSplitPolicy中的方法,别觉得奇怪,因为IncreasingToUpperBoundRegionSplitPolicy extends ConstantSizeRegionSplitPolicy,这个找不到线索就看看这个类,然后找到了如下代码

private long desiredMaxFileSize;

  @Override
  protected void configureForRegion(HRegion region) {
    super.configureForRegion(region);
    Configuration conf = getConf();
    HTableDescriptor desc = region.getTableDesc();
    if (desc != null) {
      this.desiredMaxFileSize = desc.getMaxFileSize();
    }
    //设置desiredMaxFileSize = hbase.hregion.max.filesize的大小默认是10G
    if (this.desiredMaxFileSize <= 0) {
      this.desiredMaxFileSize = conf.getLong(HConstants.HREGION_MAX_FILESIZE,
        HConstants.DEFAULT_MAX_FILE_SIZE);
    }
    //如果设置了hbase.hregion.max.filesize.jitter  则desiredMaxFileSize做个抖动
    float jitter = conf.getFloat("hbase.hregion.max.filesize.jitter", Float.NaN);
    if (!Float.isNaN(jitter)) {
      this.desiredMaxFileSize += (long)(desiredMaxFileSize * (RANDOM.nextFloat() - 0.5D) * jitter);
    }
  }

原来如果设置了hbase.hregion.max.filesize.jitter,则用HREGION_MAX_FILESIZE + HREGION_MAX_FILESIZE*随机小数*hbase.hregion.max.filesize.jitter,其中jitter默认为0.5,HREGION_MAX_FILESIZE 其实就是hbase.hregion.max.filesize,默认是10G,至于为什么抖动,有的人说是为了防止重启regionServer时进行大量的major compact,这种说法我暂时不明白,先放一放。

回到shouldSplit方法中,我们看看canSplit方法做了什么?

@Override
  public boolean canSplit() {
    this.lock.readLock().lock();
    try {
      // Not split-able if we find a reference store file present in the store.
      boolean result = !hasReferences();
      if (!result && LOG.isDebugEnabled()) {
        LOG.debug("Cannot split region due to reference files being there");
      }
      return result;
    } finally {
      this.lock.readLock().unlock();
    }
  }

很简单,就是看看有没有引用文件,如果有则不能split,如果没有则可以,再次回到shouldSplit方法,可以看到如果当前的store的大小大于刚刚计算出的阀值,则返回true,算是通过split的判断了。

好的,来总结一下:

hbase对一个region切分,有几个条件:
1、如果是用户请求切分,则不管什么情况都可以切分。
2、如果非用户请求,并且这个region中任意store含有引用文件,则不切分
3、如果不是用户请求,也没有引用文件,则判断每个store的大小,只要其中有一个大于阀值,则切分。这个阀值在上面已经有说到。

说下这个策略的含义
0.94版本之前使用的是ConstantSizeRegionSplitPolicy策略,此策略只是大于一个基本固定的阀值就允许split,而现在的策略则是store大小大于一个变化的阀值就允许split,什么意思呢,举个例子,当hbase相关split的属性都没有配置,采用默认,一张表刚建立,默认情况只有1个region,那么逻辑上是当这个region的store大小超过 1*1*1*flushsize*2 = 128M *2 =256M时 才会允许split,如果达到这个值切分后,会有两个region,其中一个region中的某个store大小大于 2*2*2*flushsize*2 = 2048M 时,则允许split,如此计算下去,直到这个大小超过了hbase.hregion.max.filesize+ hbase.hregion.max.filesize*随机小数*hbase.hregion.max.filesize.jitter才允许split,基本也就固定了,如果粗劣的计算可以把这个hbase.hregion.max.filesize的大小作为最后的阀值,默认是10G,也就说当这个阀值变化到10G,这个阀值就基本上不再变化。

这种思想使得阀值达到一个基本固定的值之前先做了几次split,而这几次split的数据量很少,对hbase的影响也没有那么大,而且相当于数据导入量不大的时候就做了一次“预分region”,在一定意义上减少了以后的热点region的发生。

时间: 2024-10-12 03:15:25

hbase split 源码分析之split策略的相关文章

hbase RPCServer源码分析

前置知识: java,nio,多线程 看了几天的源码,写一些自己心得,若有错误请指出. RPCServer的作用:负责创建listener,reader,responser,handler来处理client端的请求. RPCServer中重要的子类有:Listener,Reader,Call,Connection,Responser 其中Reader是Listener的子类 listener负责监听client端的请求,主要做nio操作中的accept操作. while (iter.hasNex

Solr4.8.0源码分析(25)之SolrCloud的Split流程

Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大致也了解split的原理了,所以也就有了这篇文章.本系列有两篇文章,第一篇为core split,第二篇为collection split. 1. 简介 这里首先需要介绍一个比较容易混淆的概念,其实Solr的HTTP API 和 SolrCloud的HTTP API是不一样,如果接受到的是Solr的

Java split方法源码分析

Java split方法源码分析 1 public String[] split(CharSequence input [, int limit]) { 2 int index = 0; // 指针 3 boolean matchLimited = limit > 0; // 是否限制匹配个数 4 ArrayList<String> matchList = new ArrayList<String>(); // 匹配结果队列 5 Matcher m = matcher(inp

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三)

Solr4.8.0源码分析(22)之 SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及PeerSync策略.本文以及后续的文章将重点介绍Replication策略.Replication策略不但可以在SolrCloud中起到leader到replica的数据同步,也可以在用多个单独的Solr来实现主从同步.本文先介绍在SolrCloud的leader到replica的数据同步,下一篇

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了.本文主要介绍Solr的主从同步复制.它与前文<Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)>略有不同,前文讲到的是SolrCloud的leader与replica之间的同步,不需要通过配置solrconfig.xml来实现.而本文主要介绍单机模式下,利用so

Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)

Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面三章分别介绍了Recovery的原理和总体流程,PeerSync策略,Replication策略.本章主要介绍我在实际生产环境中碰到的recovery的几个问题,以及前面漏下的几个点. 一. 日志中多次出现"Stopping recovery for zkNodeName= ..." 我在

Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二)

Solr4.8.0源码分析(21)之SolrCloud的Recovery策略(二) 题记:  前文<Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)>中提到Recovery有两种策略,一是PeerSync和Replication.本节将具体介绍下PeerSync策略. PeeySync是Solr的优先选择策略,每当需要进行recovery了,Solr总是会先去判断是否需要进入PeerSync,只有当PeerSync被设置为跳过或者PeerSync时候发现没符合

Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一)

Solr4.8.0源码分析(20)之SolrCloud的Recovery策略(一) 题记: 我们在使用SolrCloud中会经常发现会有备份的shard出现状态Recoverying,这就表明SolrCloud的数据存在着不一致性,需要进行Recovery,这个时候的SolrCloud建索引是不会写入索引文件中的(每个shard接受到update后写入自己的ulog中).关于Recovery的内容包含三篇,本文是第一篇介绍Recovery的原因以及总体流程. 1. Recovery的起因 Rec

cocos2d-x 源码分析 : Ref (CCObject) 源码分析 cocos2d-x内存管理策略

源码版本来自3.x,转载请注明 cocos2d-x 源码分析总目录: http://blog.csdn.net/u011225840/article/details/31743129 1.Ref,AutoreleasePool,PoolManager Ref中包含了一个叫referenceCount的引用计数,当一个Ref类的变量被new的时候,其referenceCount的引用计数被置为1. 其中有三个重要的操作,retain,release,autorelease,下面源码分析时会详细说明