`
无尘道长
  • 浏览: 160793 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Split Region详细解读

阅读更多

//HRegionServer类的splitRegion方法,该方法封装了手动split的实现逻辑

public void splitRegion(HRegionInfo regionInfo, byte[] splitPoint)

      throws NotServingRegionException, IOException {

    region.flushcache(); //刷新memstore

region.forceSplit(splitPoint);//设置splitRequest状态为true,以便强制split

//通过线程池完成split,线程池大小通过hbase.regionserver.thread.split配置(默认为1),具体的split逻辑请参阅接下来的SplitRequestrun方法

    compactSplitThread.requestSplit(region, region.checkSplit());

}

 

public byte[] checkSplit() {

    // Can't split ROOT/META

    if (this.regionInfo.isMetaTable()) {

      return null;

    }

 

    //如果splitRequest=true或者发现一个大小达到split条件的store(注意:一个store对应一个列族,有多个storefile文件和一个Memstore)则返回true,如果不是强制splitsplitRequest=false)且该region下存在一个storefilereference类型的文件则不能splitreference文件只会存在split期间)

    if (!splitPolicy.shouldSplit()) {

      return null;

    }

  

   //如果在split时指定了rowkey直接返回,否则执行以下逻辑:循环所有的store,找出最大并且不包含referencestore,采用该storesplitPoint作为regionsplitPointstoresplitPoint采用最大的storeFile,的中间rowKey作为splitPoint,因此split实际上并不是完全的等分,可能一个region会比另一个大很多

    byte[] ret = splitPolicy.getSplitPoint();

 

    if (ret != null) {

      try {

        checkRow(ret, "calculated split");

      } catch (IOException e) {

        LOG.error("Ignoring invalid split", e);

        return null;

      }

    }

    return ret;

  }

 

//SplitRequestrun方法

public void run() {

    SplitTransaction st = new SplitTransaction(parent, midKey);

    if (!st.prepare()) return;

    st.execute(this.server, this.server);

  }

 

// SplitTransactionprepare方法

public boolean prepare() {

//如果regionclose或者正在close或者有reference类型的storefile则不split,只有split过程中才会产生referencestorefile文件

    if (!this.parent.isSplittable()) return false;

HRegionInfo hri = this.parent.getRegionInfo();

byte [] startKey = hri.getStartKey();

    byte [] endKey = hri.getEndKey();

long rid = getDaughterRegionIdTimestamp(hri);

//创建两个新的region对象

    this.hri_a = new HRegionInfo(hri.getTableName(), startKey, this.splitrow, false, rid);

    this.hri_b = new HRegionInfo(hri.getTableName(), this.splitrow, endKey, false, rid);

    return true;

  }

 

// SplitTransactionexcuete方法

public PairOfSameType<HRegion> execute(final Server server,

      final RegionServerServices services)

  throws IOException {

    //关闭被splitregion,生成两个新的regionA B

PairOfSameType<HRegion> regions = createDaughters(server, services);

//打开split后生成的A B两个region

openDaughters(server, services, regions.getFirst(), regions.getSecond());

//修改zk中保存的split状态由splitingsplitmaster获知该状态后会删除zk中的该状态节点

该方法会在循环中检测master是否获得了通知,每次检测会休眠100ms

    transitionZKNode(server, services, regions.getFirst(), regions.getSecond());

    return regions;

  }

 

PairOfSameType<HRegion> createDaughters(final Server server,

      final RegionServerServices services) throws IOException {

//设置split超时时间(默认为3000ms,可通过hbase.regionserver.fileSplitTimeout项配置)

    boolean testing = server == null? true:

      server.getConfiguration().getBoolean("hbase.testing.nocluster", false);

    this.fileSplitTimeout = testing ? this.fileSplitTimeout :

      server.getConfiguration().getLong("hbase.regionserver.fileSplitTimeout", this.fileSplitTimeout);

 

 //zk创建一个临时的节点,保存split状态:

    RS_ZK_REGION_CLOSED       (2),   // RS has finished closing a region

    RS_ZK_REGION_OPENING      (3),   // RS is in process of opening a region

    RS_ZK_REGION_OPENED       (4),   // RS has finished opening a region

    RS_ZK_REGION_SPLITTING    (5),   // RS has started a region split

    RS_ZK_REGION_SPLIT        (6),   // RS split has completed.

    RS_ZK_REGION_FAILED_OPEN  (7),   // RS failed to open a region

    if (server != null && server.getZooKeeper() != null) {

      try {

        createNodeSplitting(server.getZooKeeper(), this.parent.getRegionInfo(), server.getServerName());

      } catch (KeeperException e) {

        throw new IOException("Failed creating SPLITTING znode on " +this.parent.getRegionNameAsString(), e);

      }

    }

    this.journal.add(JournalEntry.SET_SPLITTING_IN_ZK);

    if (server != null && server.getZooKeeper() != null) {

      try {

        this.znodeVersion = transitionNodeSplitting(server.getZooKeeper(),

            this.parent.getRegionInfo(), server.getServerName(), -1);

      } catch (KeeperException e) {

        throw new IOException("Failed setting SPLITTING znode on " + this.parent.getRegionNameAsString(), e);

      }

}

//hdfs创建一个split目录:{ region dir}/splits

    createSplitDir(this.parent.getFilesystem(), this.splitdir);

    this.journal.add(JournalEntry.CREATE_SPLIT_DIR);

 

    List<StoreFile> hstoreFilesToSplit = null;

    Exception exceptionToThrow = null;

try{

//关闭当前region,不再提供任何服务,在关闭region前会等待该region的所有compactflush均完成(compactflush是异步的操作),并且如果发现memstore的大小等于或者大于hbase.hregion.preclose.flush.size配置的值(默认为5M)时,会进行preFlushclose前的flush),这些操作完成后才设置regionclosing状态为true,设置closing状态后会分配一个writelock,此时拒绝任何服务了,直到关闭完全,设置closedtrue,最后释放写锁

      hstoreFilesToSplit = this.parent.close(false);

    } catch (Exception e) {

      exceptionToThrow = e;

    }

    //RSonlineRegions中删除被splitregion,在下线region后的这段时间里client请求该region的数据会失败,client会不断尝试(尝试时间间隔会随着次数的增加而增大,前几次为采用hbase.client.pause配置的值,默认值为1000ms)直到split后形成的A B region上线,此过程比较快,没有复杂的处理,总之split时可能会出现超过1秒的访问。

    if (!testing) {

      services.removeFromOnlineRegions(this.parent.getRegionInfo().getEncodedName());

    }

    this.journal.add(JournalEntry.OFFLINED_PARENT);

 

//通过线程池split storeFile,池大小=storeFile个数,具体的split逻辑请参阅StoreFileSplittersplitStoreFile()

    splitStoreFiles(this.splitdir, hstoreFilesToSplit);

    this.journal.add(JournalEntry.STARTED_REGION_A_CREATION);

    HRegion a = createDaughterRegion(this.hri_a, this.parent.rsServices);

    this.journal.add(JournalEntry.STARTED_REGION_B_CREATION);

    HRegion b = createDaughterRegion(this.hri_b, this.parent.rsServices);

    this.journal.add(JournalEntry.PONR);

 

if (!testing) {

  //.META.表中下线splitregion,修改.META.表的该region信息,把offline split设置为true,并且添加两列:splitAsplitB

      MetaEditor.offlineParentInMeta(server.getCatalogTracker(),

        this.parent.getRegionInfo(), a.getRegionInfo(), b.getRegionInfo());

    }

    return new PairOfSameType<HRegion>(a, b);

  }

 

//StoreFileSplitter类的splitStoreFile逻辑

private void splitStoreFile(final StoreFile sf, final Path splitdir)

  throws IOException {

    FileSystem fs = this.parent.getFilesystem();

    byte [] family = sf.getFamily();

    String encoded = this.hri_a.getEncodedName();

    Path storedir = Store.getStoreHomedir(splitdir, encoded, family);

    StoreFile.split(fs, storedir, sf, this.splitrow, Range.bottom);

    encoded = this.hri_b.getEncodedName();

    storedir = Store.getStoreHomedir(splitdir, encoded, family);

    StoreFile.split(fs, storedir, sf, this.splitrow, Range.top);

  }

// StoreFilesplit方法

static Path split(final FileSystem fs,

                    final Path splitDir,

                    final StoreFile f,

                    final byte [] splitRow,

                    final Reference.Range range)

      throws IOException {

    // 检查splitrow是否是在该store file范围内

    if (range == Reference.Range.bottom) {

      KeyValue splitKey = KeyValue.createLastOnRow(splitRow);

      byte[] firstKey = f.createReader().getFirstKey();

      if (f.getReader().getComparator().compare(splitKey.getBuffer(),

          splitKey.getKeyOffset(), splitKey.getKeyLength(),

          firstKey, 0, firstKey.length) < 0) {

        return null;

      }     

    }

    else {

      KeyValue splitKey = KeyValue.createFirstOnRow(splitRow);

      byte[] lastKey = f.createReader().getLastKey();     

      if (f.getReader().getComparator().compare(splitKey.getBuffer(),

          splitKey.getKeyOffset(), splitKey.getKeyLength(),

          lastKey, 0, lastKey.length) > 0) {

        return null;

      }

    }

//reference store,生成的reference stroe的名称格式例如:0959f79e6f7141aba1d81450ac891884.a00556374994fa5b3369e884b45492d3,其中a00556374994fa5b3369e884b45492d3为被splitregionid0959f79e6f7141aba1d81450ac891884为引用的storeFile的名称,该引用的storefile的内容是用于split的中间rowkey,两个新的regionreference文件的个数均与split regionstoreFile文件个数相同,通过hbase的管理界面看到

    Reference r = new Reference(splitRow, range);

    String parentRegionName = f.getPath().getParent().getParent().getName();

    Path p = new Path(splitDir, f.getPath().getName() + "." + parentRegionName);

    return r.write(fs, p);

  }

 

//打开split生成的两个region

void openDaughters(final Server server,

      final RegionServerServices services, HRegion a, HRegion b)

      throws IOException {

      DaughterOpener aOpener = new DaughterOpener(server, a);

      DaughterOpener bOpener = new DaughterOpener(server, b);

      aOpener.start();

      bOpener.start();

      aOpener.join();//等待open完成

      bOpener.join();

      //如果包含referencestorefile或者storefile数量超过了配置的限制大小时会requestCompactcompact后,reference文件变为实际文件

      //增加region信息到.META.

      services.postOpenDeployTasks(b, server.getCatalogTracker(), true);

//添加region对象到RegionServeronlineRegions列表中,此时可以对外服务

      services.addToOnlineRegions(b);

      services.postOpenDeployTasks(a, server.getCatalogTracker(), true);

      services.addToOnlineRegions(a);

    }

  }

分享到:
评论

相关推荐

    (003)HashMap中红黑树TreeNode的split方法源码解读.docx

    HashMap 中红黑树 TreeNode 的 split 方法源码解读 HashMap 中红黑树 TreeNode 的 split 方法是 Java 中HashMap 的核心组件之一,负责将红黑树从旧数组转移到新数组上,并进行树链表的重新组织和优化。在本文中,...

    C#中Split用法详细介绍

    在本文中,我们将详细探讨`Split`方法的各种用法,以及如何在不同场景下有效地使用它。 首先,让我们回顾一下提供的示例: 1. 用字符串分隔: ```csharp string str = "aaajsbbbjsccc"; string[] sArray = ...

    ip2region.xdb

    本文将详细探讨如何在C#环境中利用ip2region组件实现这一功能。 首先,我们需要理解ip2region的核心原理。ip2region采用二分查找算法,存储了全球IP地址与地理位置、运营商的对应关系,数据存储在一个名为"xdb"的二...

    SplitButton 分裂按钮 WPF

    在WPF(Windows Presentation Foundation)中,SplitButton是一种特殊的按钮控件,它结合了普通按钮和下拉菜单的功能。SplitButton的设计理念是为了提供更丰富的交互体验,用户不仅可以点击按钮执行主要操作,还可以...

    Oracle中实现Split函数功能

    下面是实现 Oracle 中 Split 函数功能的详细介绍。 首先,需要创建一个新的类型来存储拆分后的结果。这个类型是一个表类型,用于存储拆分后的字符串数组。创建类型的 SQL 语句如下所示: ``` CREATE OR REPLACE ...

    split_dos(LInux)

    split_dos

    C# vs2019 实现SplitContainer 上下左右 折叠 隐藏与显示

    以下是对这个主题的详细解释: 1. **SplitContainer控件介绍**: SplitContainer控件由两部分组成,通常包含两个Panel,可以水平或垂直分割。每个Panel可以独立调整大小,也可以固定大小。用户可以通过拖动分隔线...

    C#中Split用法

    下面将详细介绍`Split`方法的各种用法及其应用场景。 #### 一、基本概念 `Split`方法定义于`System.String`类中,它允许用户通过不同的分隔符将字符串拆分成子字符串。这些子字符串会被存储在一个数组中以便进一步...

    Ext4详细解读

    ### Ext4详细解读 #### 一、Accordion布局详解 Accordion布局是一种特殊类型的布局方式,它在用户界面设计中被广泛采用。Accordion布局也被称为“手风琴”布局,这种布局的特点是在任何时间里,只有一个面板处于...

    SplitContainer带箭头收缩

    SplitContainer控件是Windows Forms和WPF等GUI框架中常用的一种布局组件,它允许开发者在界面上创建可调整大小的面板区域。在这个特定的主题“SplitContainer带箭头收缩”中,我们关注的是如何通过添加箭头图形和...

    oracle split函数

    本文将详细介绍两种在Oracle中实现字符串分割的方法:第一种是通过创建类型和管道函数的方式;第二种则是通过循环和子字符串处理实现的。 #### 二、第一种实现方式:创建类型和管道函数 ##### 1. 创建类型 `TY_STR...

    第八个范例——布局之SplitContainer

    6. 博文链接提供了更详细的教程,包括如何将SplitContainer整合到实际项目中,以及可能遇到的问题和解决方案。 7. “eight.html”文件是一个HTML教程,包含了关于如何使用SplitContainer的详细说明和可能的代码示例...

    C# vs2010 实现SplitContainer(上下左右)隐藏与显示

    SplitContainer控件是Windows Forms中的一个重要组件,它允许用户在一个容器内划分两个或更多个区域,这些区域可以水平(左右)或垂直(上下)分割,并且可以根据需要进行隐藏和显示。本篇文章将深入探讨如何在C# VS...

    pb split() 字符串分割函数

    下面我们将详细探讨自定义`split()`函数可能涉及的知识点: 1. **参数**:自定义`split()`函数通常包含两个主要参数:原始字符串和分隔符。原始字符串是待分割的文本,分隔符是用于识别何处切割字符串的标志。有的...

    HBASERegion数量增多问题描述及解决方案.docx

    新表创建时,默认只有一个Region,随着数据增加,Region会自动Split,以保持数据的分散存储。关注Region数量的主要原因是避免对集群稳定性和性能产生负面影响。 1.2 Region过多的影响 - 频繁刷写:Region过多可能...

    万能视频切割合并软件——SplitIt

    【SplitIt:万能视频切割合并软件】 SplitIt是一款功能强大的视频处理工具,以其高效、易用和广泛的格式支持在用户中赢得了良好的口碑。这款软件的主要功能包括视频切割和视频合并,使得用户能够轻松地对视频进行...

    SDLXLIFF file Split and Merge

    "SplitSDLXLIFF.msi" 是一个安装程序,用于安装一个名为“Split SDLXLIFF”的免费软件。这个工具专门设计用于帮助用户高效地处理Trados Studio的任务,特别是在需要对大文件进行精细化管理的情景下。拆分SDLXLIFF...

    wpf 中炫酷的分裂按钮SplitButton Demo

    在Windows Presentation Foundation(WPF)中,SplitButton是一种特殊的按钮控件,它结合了普通按钮的功能和下拉菜单的功能。这个“wpf 中炫酷的分裂按钮SplitButton Demo”示例将向我们展示如何在WPF应用中创建并...

Global site tag (gtag.js) - Google Analytics