当前位置: 首页 > 科技观察

HBase原理–所有Region切分的细节都在这里了

时间:2023-03-12 05:56:35 科技观察

HBase原理——Region切分的所有细节,这里都是良药。HBase系统中Region的自动切分是如何实现的?这涉及到很多知识点,比如Region分割的触发条件是什么?Region分割的分割点在哪里?可用性?拆分过程中出现异常如何处理?是否要在拆分过程中移动数据?等等,本文将对这些细节做一个基本的解释。一方面可以让你对HBase中的Region自动切分有更深入的了解,另一方面,如果你想实现类似的功能,也可以参考HBase的实现。Region切分触发策略在最新的稳定版(1.2.6)中,HBase已经拥有多达6种切分触发策略。当然,每种触发策略都有其适用的场景,用户可以根据业务选择不同的表级分段触发策略。常见的拆分策略如下图所示:ConstantSizeRegionSplitPolicy:0.94版本之前的默认拆分策略。这是最容易理解但也是最误导人的细分策略。从字面上看,当region的大小大于某个阈值(hbase.hregion.max.filesize)时,就会触发切分。其实并不是这样的,这个阈值的实际实现是针对某个店铺的,也就是只有在某个区域内***店铺的规模大于设定的阈值后才会触发切分。还有一个大家比较关心的问题就是这里说的storesize到底是压缩文件的总大小还是解压文件的总大小。在实际实现中,storesize就是压缩文件(压缩场景)的大小。ConstantSizeRegionSplitPolicy是比较容易想到的,但是这种在生产线上的拆分策略有相当大的缺点:拆分策略没有明显的大表和小表的区分。较大的阈值(hbase.hregion.max.filesize)设置对大表更友好,但小表可能不会触发拆分。在极端情况下,可能只有一个,这对业务来说不是一件好事。如果设置的小,对小表比较友好,但是大表会在整个集群中产生大量的region,对于集群管理、资源使用、故障转移都不是一件好事。IncreasingToUpperBoundRegionSplitPolicy:0.94~2.0版本默认的拆分策略。这种拆分策略稍微复杂一些。总的来说和ConstantSizeRegionSplitPolicy的思路是一样的。一个region中***store的大小大于设定的阈值触发分裂。不过这个阈值并不是像ConstantSizeRegionSplitPolicy那样是一个固定值,而是会在一定条件下不断调整。调整规则与region表中当前regionserver的region个数有关:(#regions)*(#regions)*(#regions)*flushsize*2,当然threshold不会增加到最大值,最大值为用户设置的MaxRegionFileSize。这种切分策略弥补了ConstantSizeRegionSplitPolicy的不足,可以适应大表和小表。而且在大集群的情况下,对很多大表表现的很好,但并不完美。在这种策略下,很多小表会在大集群中产生大量的小区域,这些小区域分散在整个集群中。此外,当发生区域迁移时,也可能触发区域分裂。SteppingSplitPolicy:2.0版本的默认拆分策略。这个切分策略的切分阈值又变了。与IncreasingToUpperBoundRegionSplitPolicy相比,更加简单。还是和要拆分的region所属表中当前regionserver上的region个数有关。如果region个数等于1,分割阈值为flushsize*2,否则为MaxRegionFileSize。这种切分策略比IncreasingToUpperBoundRegionSplitPolicy对大集群中的大小表更加友好。小表不会再产生大量的小区域,但是适可而止。此外,还有一些其他的分裂策略,比如使用DisableSplitPolicy:可以禁止region分裂;而KeyPrefixRegionSplitPolicy和DelimitedKeyPrefixRegionSplitPolicy对于拆分策略仍然遵循默认的拆分策略,只是对拆分点有自己的看法。例如,KeyPrefixRegionSplitPolicy要求相同的PrefixKey留在一个区域中。在使用上,一般情况下可以使用默认的分割策略,也可以在cf层面设置区域分割策略。命令是:create'table',{NAME=>'cf',SPLIT_POLICY=>'org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy'}区域分裂准备——寻找SplitPoint区域分裂策略会触发区域分裂。分裂开始后的第一件事就是找到分裂点-splitpoint。所有默认的拆分策略,无论是ConstantSizeRegionSplitPolicy、IncreasingToUpperBoundRegionSplitPolicy还是SteppingSplitPolicy,都具有相同的拆分点定义。当然,当用户手动执行分割时,也可以指定分割点进行分割,这里不做讨论。分割点是如何定位的?整个区域的***store中***文件中最中心块的***rowkey。这是一个比较耗脑的说法,需要细细品味。另外,HBase还规定,如果定位到的rowkey是整个文件的最后一个rowkey或者最后一个rowkey,则认为没有切分点。什么情况下会出现没有分割点的场景?最常见的是一个文件只有一个block,执行split的时候会发现无法split。很多新同学在测试split的时候,往往会新建一张表,往新表中插入几条数据,执行flush,然后执行split。奇迹般地,他们发现数据表实际上并没有拆分。原因就在这里。如果此时仔细查看debug日志,可以看到这样一条logdrop:RegioncoresegmentationprocessHBase将整个切分过程打包成一个事务,意在保证切分事务的原子性。整个splittransaction过程分为prepare-execute-(rollback)三个阶段,操作模板如下:prepare阶段:在内存中初始化两个子region,具体生成两个HRegionInfo对象,包括tableName,regionName,startkey,结束键等。同时,将生成交易日志。该对象用于记录分割的进度。具体参见回滚阶段。执行阶段:切分的核心操作。见下图(来自Hortonworks):regionserver将ZK节点/region-in-transition中region的状态改为SPLITING。master通过watchnode/region-in-transition检测region的状态变化,修改内存中region的状态。masterpage上的RIT模块可以看到region拆分执行的状态信息。在父存储目录下新建一个临时文件夹。split保存分割后的子区域信息。关闭父region:parentregion关闭数据写入,触发flush操作,将写入region的所有数据持久化到磁盘。一段时间后,客户端对父区域的请求会抛出异常NotServingRegionException。核心拆分步骤:在.split文件夹下创建两个子文件夹,分别为daughterA和daughterB,并在文件夹中生成reference文件,指向parentregion中对应的文件。这一步是所有步骤的核心。生成参考文件的日志如下:2017-08-1211:53:38,158DEBUG[StoreOpener-0155388346c3c919d3f05d7188e885e0-1]regionserver.StoreFileInfo:reference'hdfs://hdfscluster/hbase-rsgroup/data/default/music/0155388346c3c919d3f05d7188e885e0/cf/d24415c4fb44427b8f698143e5c4d9dc.00bb6239169411e4d0ecb6ddfdbacf66'toregion=00bb6239169411e4d0ecb6ddfdbacf66hfile=d24415c4fb44427b8f698143e5c4d9dc。Amongthem,thereferencefilenameisd24415c4fb44427b8f698143e5c4d9dc.00bb6239169411e4d0ecb6ddfdbacf66,andtheformatlooksspecial.这个文件名是什么意思?我们来看看引用文件指向的父区域文件。根据日志,splitparentregion为00bb6239169411e4d0ecb6ddfdbacf66,对应splitfile为d24415c4fb44427b8f698143e5c4d9dc。可以看出,参考文件名是一种信息量很大的命名方式,如下图:另外,还需要注意参考文件的文件内容。参考文件是参考文件(不是linux链接文件),文件内容显然不是用户数据。文件内容其实很简单,主要由两部分组成:一是splitkey,二是boolean类型的变量(true或false),true表示参考文件是指parent的上半部分文件(top),false表示引用了下半部分(bottom)。为什么要存储这两部分?下面就来听听分解吧。看官可以使用hadoop命令亲自来查看reference文件的具体内容:hadoopdfs-cat/hbase-rsgroup/data/default/music/0155388346c3c919d3f05d7188e885e0/cf/d24415c4fb44427b8f698143e5c4d9dc.00bb6239169411e4d0ecb6ddfdbacf666.父region划分为两个子region后,将daughterA、daughterB复制到HBase根目录下,形成两个新的region。7、修改hbase.meta表后parentregion将下线,不再提供服务。下线后,parentregion的metatable中的信息不会立即删除,而是将splitcolumn和offlinecolumn标记为true,记录这两个sub-region。为什么不立即删除它?并听听下面的分解。8.启用daughterA和daughterB子区域。通知修改hbase.meta表,正式对外提供服务。回滚阶段:如果执行阶段出现异常,则执行回滚操作。为了实现回滚,将整个切分过程分成了很多子阶段,回滚程序会根据当前进度到了哪个子阶段,来清理相应的垃圾数据。代码使用JournalEntryType来表示各个子阶段,如下图:childregion,并且系统中有很多子步骤,比如meta元数据的变化,所以必须保证整个切分过程的事务性,即要么切分完全成功,要么切分还没有开始全部,而且无论如何,分割都不能中途完成。为了实现事务性,hbase设计了一个状态机(见SplitTransaction类)来保存拆分过程中每个子步骤的状态,这样一旦出现异常,系统可以根据当前的情况决定是否回滚状态,以及如何回滚。不幸的是,在目前的实现中,这些中间状态只保存在内存中,所以一旦在切分过程中regionserver宕机,切分可能处于中间状态,即RIT状态。在这种情况下,您需要使用hbck工具来详细查看和分析解决方案。在2.0版本之后,HBase实现了一个新的分布式事务框架ProcedureV2(HBASE-12439)。新的框架会使用HLog来存储这个单机事务的中间状态(DDL操作、Split操作、Move操作等),这样就可以保证即使一个参与者在事务执行过程中宕机了,HLog也可以仍然作为协调者回滚事务或重试提交,大大减少甚至消除RIT现象。这也是2.0在易用性方面最值得期待的亮点!!!Region切分对其他模块的影响通过对region切分过程的了解,我们知道整个region切分过程不涉及数据移动,所以切分本身的代价不是很高,可以很快完成。拆分后,分区域的文件实际上没有任何用户数据,文件中只存储了一些元数据信息-切分点rowkey等。如何通过引用文件找到数据呢?子区域的数据什么时候真正完成真正的迁移?数据迁移完成后父区域什么时候删除?1、如何通过引用文件查找数据?在这里您将看到引用文件名和文件内容的实际含义。整个过程如下图所示:(1)根据参考文件名(区域名+真实文件名),定位到真实数据所在的文件路径(2)定位到真实数据文件后,可以在整个文件中扫描要检查的KV?不会。因为引用文件通常只引用数据文件中的一半数据,以分割点为界,要么是文件数据的上半部分,要么是文件数据的下半部分。那么是哪部分数据呢?哪个点是分割点?记住上面提到的reference文件的文件内容,没错,就是文件中记录的。2、父区域的数据什么时候迁移到子区域目录?答案是在子区域发生major_compaction时。我们知道compaction的执行其实就是将store中的所有小文件一个一个KV,一个KV从小到大读取,然后依次写入一个大文件,完成后删除小文件,所以compaction本身需要读写Enter大量的数据。子区域执行major_compaction后,会读取父目录下所有属于子区域的数据,写入子区域目录的数据文件中。可见将数据迁移放在compaction阶段是为了方便。3、parentregion什么时候删除?实际上,HMaster会启动一个线程,周期性的遍历检查所有处于分裂状态的parentregion,来判断parentregion是否可以被清理掉。检测线程会先在metatable中找到所有splitcolumn为true的region,加载split后生成的两个子region(metatable中的columnsplitA和splitB),只需要检查这两个subregion是否-regions仍然存在引用文件,如果没有引用文件,可以认为父region对应的文件可以删除。现在看看上面meta表中父目录的信息,大概就能明白为什么要存这个信息了:4.产线拆分模块的一些坑?有时同学们会反映集群中的某些region处于长期RIT中,regionstateissplitting。Undernormalcircumstances,itisrecommendedtousehbcktoseewhaterrorisreported,andthenrepairitaccordingtosometoolsprovidedbyhbck.hbckprovidessomecommandstorepairtheritregioninthesplitstate.Themaincommandsareasfollows:-fixSplitParentsTrytoforceofflinesplitparentstobeonline.-removeParentsTrytoofflineandsidelinelingeringparentsandkeepdaughterregions.-fixReferenceFilesTrytoofflinelingeringreferencestorefiles其中最常见的问题是:ERROR:Foundlingeringreferencefilehdfs://mycluster/hbase/news_user_actions/3b3ae24c65fc5094bc2acfebaa7a56de/meta/0f47cda55fa44cf9aa2599079894aed6.b7b3faab86527b88a92f2a248a54d3dc”简单解释一下,这个错误是说reference文件所引用的父region文件不存在了,如果查看Inthelog,youmayseethefollowingexception:java.io.IOException:java.io.IOException:java.io.FileNotFoundException:Filedoesnotexist:/hbase/news_user_actions/b7b3faab86527b88a92f2a248a54d3dc/meta/0f47cda55fa44cf9aa2599079894aedWhydoesthefiledonotexistaftertheparentregion?Discussionwithfriends,itisconfirmedthatitmaybecausedbyanofficialbug,seeHBASE-13331fordetails.ThisjirameansthatwhenHMasterconfirmswhethertheparentdirectorycanbedeleted,ifitchecksthereferencefile(checkwhetheritexists,checkwhetheritcanbeopenednormally)throwIfanIOExceptionoccurs,thefunctionwillreturnnoreferencedfile,causingtheparentregiontobedeleted.Undernormalcircumstances,itshouldbesafetoreturnthereferencedfile,keeptheparentregion,andprintthelogformanualintervention.如果你也遇到类似的问题,可以看看这个问题,也可以打在线版的修复补丁或者升级版本。