HBase的split分析

HBase在新建一个表的时候,默认会把所有数据都会放在一个HRegion上,主节点HMaster根据一定的策略把HRegion分配到不同的HRegionServer从节点上,客户端在进行读写操作的时候,就会访问对应HRegionServer的HRegion。当HRegion的数据量超过阀值的时候,为了防止单个热点访问带来的压力,HBase就会对HRegion进行split操作,一个父HRegion分为两个子HRegion,后续的数据写入操作就会分配到两个HRegion里,减轻了单个热点的负载。由此可以看到split是一种动态的负载平衡机制。如果对数据库知识有过一定了解,就会发现这就是数据库常用的sharding技术,只不过HBase提供了自动化处理,减轻维护开销。另外split会产生额外的IO,实际实践中也有手动关闭该特性,按照规模进行预先分配HRegion的做法。

具体实现

   split是把数据平均分配的操作:按照父HRegion的SplitKey(约为rowKey范围的中间值)把父HRegion分成两个子HRegion后,同时会把父HRegion的数据也会重新写入到两个子HRegion上。另外子HRegion的rowKey范围是父HRegion的各自一半,这样后面的rowKey就会按照范围插入对应的HRegion。split在执行过程中采取一些做法避免影响读写请求,由于split过程需要较长时间大量的IO操作,如果发生故障就需要有效的failover机制,防止数据处于不一致的状态。整个步骤可以参考文章的这个图,其中有些细节不同,但大致过程基本一致。

split入口

    当memstore flush操作后,HRegion写入新的HFile或者HStore刚刚进行完compact操作后,这两个操作都有可能产生较大的HFile,HBase就会调用CompactSplitThread.requestSplit判断是否需要split操作。这个判断如下:

  • 判断整个HRegionServer所有的HRegion数量是否超过hbase.regionserver.regionSplitLimit(默认Integer.MAX_VALUE,即没有限制)。
  • 当前HRegion所有HStore中包含的HFile最小数是否>=1
  • 尝试获取SplitKey:hbase:meta表(记录HRegion信息的HBase表,只有单个HRegion)、或是正在恢复状态的HRegion返回null。然后利用设置的策略判断是否需要split操作。一般使用两种策略:ConstantSizeRegionSplitPolicy以及IncreasingToUpperBoundRegionSplitPolicy(默认)。
  • ConstantSizeRegionSplitPolicy:如果某个不包含Reference文件的HStore(Reference文件是split后产生的临时引用文件,见后述),总大小(包含HFile的总大小)超过hbase.hregion.max.filesize(默认10G),则返回true。 IncreasingToUpperBoundRegionSplitPolicy:对于HRegionServer内所有属于同一个表的HRegion的数n,如果某个不包含Reference文件的HStore,总大小超过[n*n*n*2*MemStoreFlushSize和hbase.hregion.max.filesize(10G)之间最小值],则返回true。例如,对于如果n=3,则split大小为3^3*2*128M=6912M。可见如果Region数比较少的时候的可以尽早采取split。
  • 返回SplitPoint。返回HRegion里总大小最大HStore的最大HFile的中间rowKey值。
split执行

获得SplitPoint后,CompactSplitThread就把split请求放到线程池执行。整个过程的每一个步骤都会有具体的日志记录,方便在split过程中失败的回滚。具体过程如下:

1、获取zk上的表的全局读锁,默认等待600s。这是为了避免和修改表发生冲突。修改表的操作会发送到HMaster执行,HMaster会获取zk上表的全局写锁,这样两个操作就会互斥避免冲突。

2、创建split后的daughter region,A和B的HRegionInfo对象,用于后续操作。A和B的HRegion在split后对应父HRegion的每个HFile,都有一个Reference文件,内容为指示Top或Bottom的标记,表示被划分文件上/下部分,以及SplitKey。

3、创建Daughter Region。这个过程包含两部分,一个是stepsBeforePONR;另一个是修改hbase:meta表内容。stepsBeforePONR中的PONR指point of no return,也就是不可逆,在这个过程中HBase的操作都会认为是可逆的,在这之后的修改hbase:meta表内容就是非可逆的。

stepsBeforePONR

(1)创建split对应的zk节点。zk创建/hbase/region-in-transition/regionName节点,节点的data为split相关数据,type为RS_ZK_REQUEST_REGION_SPLIT。不断轮询等待HMaster更换状态为RS_ZK_REGION_SPLITTING。就是利用ZK传递region split的信息给HMaster,让HMaster获知正在执行split操作。

(2)在region的目录里创建.splits目录

(3)关闭当前region。停止flush和compact操作,并等待进行中的flush和compact完成。如果所有memstore大于5mb,则flush。并行关闭所属的HStore。

(4)并行split所有的HFiles。 分别创建daughterRegion A和B的Reference文件(不能被split,compact的时候会被删除),文件路径.splits/daughterRegionName/familyName/store?leName.RegionEncodedName,然后用PB格式写入Top/Bottom枚举以及splitKey。Reference文件会在后面的compact操作中被删除,然后才会真正把split的内容写入daughterRegion,这样延迟写入的操作可以避免发生故障需要回滚删除文件从而造成IO浪费,Reference文件非常小,不会造成很明显的影响。

(5)创建daughter region对象。 同时把A和B的regionInfo写到路径.regioninfo下,然后把之前的.splits/daughterRegionName移动到table/region目录,这样daughterRegion就和parent同级目录。然后创建A和B两个HRegion对象。

修改hbase:meta表内容

发送请求到hbase:meta表的HRegion,修改hbase:meta表的内容,表示原HRegion下线,daughterRegion A和B准备上线(暂时没有location信息,因为仍没open)。另外如果修改失败,则会进行回滚操作,把之前创建的.split目录删除,然后HRegionServer会终止服务,然后HMaster会负责清理其余的状态。

4、并行打开A,B两个HRegion。创建对应的HStore,读取Reference(Reference文件有专门的Scanner和reader来限制读取对应HFile的范围),从所有HFile里读取最大的MemStoreTS、SequenceId。HRegion成功打开后,就更新hbase:meta表中A和B的location。然后根据每个HFile的MaxSequenceId进行replay WALEdit,就是把内容重新写入HStore的memstore。如果WALEdit的logNum里小于MaxSequenceId则表明已经写入HFile(每条记录对应一个SequenceId),就会跳过。然后会请求一个异步Major
compact,从Reference生成真正的HFile(异步Major compact会清理所有的Reference,参考这里)。这个时候,这两个daughterRegion已经可以对外提供读服务。

5、 更新zk节点状态。在zk上之前创建的/hbase/region-in-transition/regionName节点的type改为RS_ZK_REGION_SPLIT,通知HMaster完成split操作。另外要注意,由于zk可能会丢失消息,因此这里需要不断循环节点状态,当节点被HMaster删除当时候才表明HMaster收到通知。

6、释放表的全局读锁

这样,在CompactSplitThread的split操作就完成,在这个时候,两个daughterRegion对外提供读请求,父HRegion的文件仍然存在。等待major compact请求完成之后,两个daughterRegion的Refernece文件就会被删除。HMaster会定期监控hbase:meta表,一旦发现存在parentRegion且daughterRegion已经没有Reference文件,则会删除parentRegion的相关内容。这样就最终完成了整个split的操作。

预Split操作

在实际实践过程中,如果可以根据业务大致确认表的数据规模,则可以使用预Split把表分成指定多个HRegion。这样做可以避免Split过程带来的IO开销,并且在大批量导入数据的时候,可以让集群的多个节点分流写请求,加快导入效率。具体做法如下:

首先是关闭Split操作。从Split的过程可以看到,在判断是否需要split的时候,有两种可选的策略,我们这里采取ConstantSizeRegionSplitPolicy,然后把hbase.hregion.max.filesize设置到一个非常大的值,这样HBase就几乎不会对任何HRegion采取split操作。

然后就是在create表的时候,需要指定多个splitKey范围,如:

hbase(main):015:0> create 'test_table', 'f1', SPLITS=> ['a', 'b', 'c']

这样就会创建4个HRegion,rowKey范围分别为[负无穷,a)、[a, b)、[b, c)、[c, 正无穷)。在导入数据的时候,客户端就会根据自己的rowKey插入到对应的范围里。当然也要注意某些热点数据会导致某个HRegion特别大,最好要监控好HRegion的读写请求数(可以从master提供的web页面查看状态)。如果发现某些热点数据,可以利用命令行手动设置这个HRegion的split操作,HBase就会对这个HRegion进行split,保持负载平衡。

总结

split操作提供了表自动sharding的功能,但这是以额外的IO消耗为代价的。我们可以根据自己的业务需求进行预split或者手动split等操作。无论是哪种,这些split操作都相对较方便,能够免除维护sharding带来的一系列数据同步问题。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-12 05:29:09

HBase的split分析的相关文章

(转)HBase工程师线上工作经验总结----HBase常见问题及分析

阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端exception,一般原因是什么?5.系统越来越慢的原因是什么?6.Hbase数据写进去,为什么会没有了,可能的原因是什么?7. regionserver发生abort,遇到最多是什么情况?8.从哪些方面可以判断HBase集群是否健康?9.为了加强HBase的安全性,你会采取哪些措施?在Tcon分布式系统测

HBase工程师线上工作经验总结----HBase常见问题及分析

阅读本文可以带着下面问题:1.HBase遇到问题,可以从几方面解决问题?2.HBase个别请求为什么很慢?你认为是什么原因?3.客户端读写请求为什么大量出错?该从哪方面来分析?4.大量服务端exception,一般原因是什么?5.系统越来越慢的原因是什么?6.Hbase数据写进去,为什么会没有了,可能的原因是什么?7. regionserver发生abort,遇到最多是什么情况?8.从哪些方面可以判断HBase集群是否健康?9.为了加强HBase的安全性,你会采取哪些措施? 在Tcon分布式系统

Hbase源码分析:RPC概况

RPC是hbase中Master,RegionServer和Client三者之间通信交流的纽带.了解hbase的rpc机制能够为通过源码学习hbase奠定良好的基础.因为了解了hbase的rpc机制能够很快通过debug深入理解hbase各种机制(比方说flush,compaction,scan等请求)的流程.同时也便于碰到问题时,通过源码分析找到原因,毕竟源码面前了无秘密. 1,RPC简介 RPC(remote procedure call)即远程过程调用.对于本地调用,定义好一个函数以后,程

HBase compaction流程分析

以下分析都是基于HBase-0.98版本. HRegionServer在启动的时候,会启动compactionChecker线程,compactionChecker会检测region是否需要compaction. 主要执行的逻辑如下: protected void chore() { for (HRegion r : this.instance.onlineRegions.values()) { if (r == null) continue; for (Store s : r.getStore

HBase的compact分析

HBase是基于LSM树存储模型的分布式NoSQL数据库.LSM树对比普遍的B+树来说,能够获得较高随机写性能的同时,也能保持可靠的随机读性能(可参考这里).在进行读请求的时候,LSM树要把多个子树(类似B+树结构)进行归并查询,对于HBase来说,这些子树就是HFile(还包括内存上的树结构MemStore).因此归并查询的子树数越少,查询的性能就越高. Compact的作用 在写请求的这篇文章里,已经介绍过对于每个写请求,都必须写入MemStore以及HLog才算完成事务提交.当MemSto

Hbase源码分析:Hbase UI中Requests Per Second的具体含义

让运维加监控,被问到Requests Per Second(见下图)的具体含义是什么?我一时竟回答不上来,虽然大概知道它是指每秒Region Server的请求数,但是具体是怎么算的呢,不清楚.于是决定通过研究源码深入了解下.下面便记录了这个过程. 1,先在代码库中全局搜索Requests Per Second关键字,发现在几个jamon结尾的文件找到了.于是google了一下,这个到底是什么东东,发现是一个模板引擎. 2,查看RegionServerListTmpl.jamon内容,需要传入参

HBASE REGION SPLIT策略

hbase 0.94.0版本中,对于region的split方式引入了一个非常方便的SplitPolicy,通过这个SplitPolicy,可以主动的干预控制region split的方式.在org.apache.Hadoop.hbase.regionserver包中,可以找到这么几个自带的splitPolicy: ConstantSizeRegionSplitPolicy, IncreasingToUpperBoundRegionSplitPolicy, and KeyPrefixRegion

HBase写请求分析

HBase作为分布式NoSQL数据库系统,不单支持宽列表,并且对于随机读写来说也具有较高的性能.在高性能的随机读写事务的同时,HBase也能保持事务的一致性.目前HBase只支持行级别的事务一致性.本文主要探讨一下HBase的写请求流程,主要基于0.98.8版本的实现. 客户端写请求    HBase提供的Java client API是以HTable为主要接口,对应其中的HBase表.写请求API主要为HTable.put(write和update).HTable.delete等.以HTabl

分布式存储系统Kudu与HBase的简要分析与对比

本文来自网易云社区 作者:闽涛 背景 Cloudera在2016年发布了新型的分布式存储系统--kudu,kudu目前也是apache下面的开源项目.Hadoop生态圈中的技术繁多,HDFS作为底层数据存储的地位一直很牢固.而HBase作为Google BigTable的开源产品,一直也是Hadoop生态圈中的核心组件,其数据存储的底层采用了HDFS,主要解决的是在超大数据集场景下的随机读写和更新的问题.Kudu的设计有参考HBase的结构,也能够实现HBase擅长的快速的随机读写.更新功能.那