Region切分准备工作-寻找SplitPoint
region切分策略会触发region切分,切分开始之后的第一件事是寻找切分点-splitpoint。所有默认切分策略,无论是ConstantSizeRegionSplitPolicy、 IncreasingToUpperBoundRegionSplitPolicy 抑或是SteppingSplitPolicy,对于切分点的定义都是一致的。当然,用户手动执行切分时是可以指定切分点进行切分的,这里并不讨论这种情况
那切分点是如何定位的呢?
整个region中最大store中的最大文件中最中心的一个block的首个rowkey 。这是一句比较消耗脑力的语句,需要细细品味。另外,HBase还规定,如果定位到的rowkey是整个文件的首个rowkey或者最后一个rowkey的话,就认为没有切分点。
什么情况下会出现没有切分点的场景呢?
最常见的就是一个文件只有一个block,执行split的时候就会发现无法切分。很多新同学在测试split的时候往往都是新建一张新表,然后往新表中插入几条数据并执行一下flush,再执行split,奇迹般地发现数据表并没有真正执行切分。
Region核心切分流程
HBase将整个切分过程包装成了一个事务,意图能够保证切分事务的原子性。
整个分裂事务过程分为三个阶段:prepare – execute – (rollback)
prepare阶段:在内存中初始化两个子region,具体是生成两个HRegionInfo对象,包含tableName、regionName、startkey、endkey等。同时会生成一个transaction journal,这个对象用来记录切分的进展,具体见rollback阶段。
execute阶段:切分的核心操作。
1.regionserver 更改ZK节点 /region-in-transition 中该region的状态为SPLITING。
2.master通过watch节点/region-in-transition检测到region状态改变,并修改内存中region的状态,在master页面RIT模块就可以看到region执行split的状态信息。
3.在父存储目录下新建临时文件夹.split保存split后的daughter region信息。
4.关闭parent region:parent region关闭数据写入并触发flush操作,将写入region的数据全部持久化到磁盘。此后短时间内客户端落在父region上的请求都会抛出异常NotServingRegionException。
5.核心分裂步骤:
在.split文件夹下新建两个子文件夹,称之为daughter A、daughter B,
并在文件夹中生成reference文件,分别指向父region中对应文件。
这个步骤是所有步骤中最核心的一个环节。
除此之外,还需要关注reference文件的文件内容,reference文件是一个引用文件(并非linux链接文件),
文件内容很显然不是用户数据。文件内容其实非常简单,
主要有两部分构成:
其一是切分点 splitkey,
其二是一个boolean类型的变量(true或者false),
true表示该reference文件引用的是父文件的上半部分(top),
而false表示引用的是下半部分 (bottom)
6.父region分裂为两个子region后, 将daughter A、daughter B拷贝到HBase根目录下,形成两个新的region。
7.parent region通知修改 hbase.meta 表后下线,不再提供服务。下线后parent region在meta表中的信息并不会马上删除,而是标注split列、offline列为true,并记录两个子region。
8.开启daughter A、daughter B两个子region。通知修改 hbase.meta 表,正式对外提供服务;
rollback阶段:
如果execute阶段出现异常,则执行rollback操作。
为了实现回滚,整个切分过程被分为很多子阶段,
回滚程序会根据当前进展到哪个子阶段清理对应的垃圾数据。
代码中使用 JournalEntryType 来表征各个子阶段