Region Split
Region大小超过阀值(可以是hbase.hregion.max.filesize也可以是建表时列族配置的大小)后会触发Split,由RegionServer负责来拆分region并且offline下线该region。随后将两个子region添加到hbase:meta并在RegionServer上打开后汇报给HMaster。默认情况下Region会自动触发split,同时也可以手动运行split命令。
Region Split可以手动split也可以自动split(建议),其中手动split region往往是在region出现热点时或者自动负载不均衡时通过手动split后move region来更好进行负载均衡。怎么进行Region呢?初步印象可以理解为折半拆分,每个子region是父region(原region)的一半。如图:
Region Split相关的参数如下:
配置参数
默认值
hbase.hregion.max.filesize |
hbase.hregion.max.filesize10737418240 (10GB) |
hbase.regionserver.region.plit.policy |
IncreasingToUpperBoundRegionSplitPolicy(0.94版本后) |
hbase.regionserver.regionSplitLimit |
1000 |
hbase.client.keyvalue.maxsize |
10485760 (10MB) |
Qunar HBase配置
hbase.regionserver.region.split.policy为org.apache.hadoop.hbase.regionserver.ConstantSizeRegionSplitPolicy hbase.hregion.max.filesize为100G,为了避免拆分IO风暴。 hbase.hstore.blockingStoreFiles为30。 hbase.regionserver.regionSplitLimit配置用来限制region的数量,默认值是1000个region。
Region Split策略
HBase region split的策略由参数hbase.regionserver.region.split.policy配置。
0.94之前默认拆分策略是org.apache.hadoop.hbase.regionserver. ConstantSizeRegionSplitPolicy,该策略是基于Region配置的最大大小,由参数hbase.hregion.max.filesize指定。当Region的StoreFiles大小之和超过了hbase.hregion.max.filesize设置时,Region将被拆分。默认值是10GB。
自0.94版本后,默认策略是org.apache.hadoop.hbase.regionserver. IncreasingToUpperBoundRegionSplitPolicy,该策略是基于RegionServer上表中region个数的平方乘以memstore size 得出split region的大小,每次split的region大小并不相同,具体算法:
regionsize = Min (R^2 * “MEMSTORE_FLUSHSIZE”||”hbase.hregion.memstore.flush.size”,”hbase.hregion.max.filesize”),最大为hbase.hregion.max.filesize。
其中R为region所在regionserver上该table的region数。MEMSTORE_FLUSHSIZE为建表是DDL定义的大小,如果没有指定则使用hbase.hregion.memstore.flush.size 。
默认hbase-site文件配置hbase.hregion.memstore.flush.size 设定大小为128M,hbase.hregion.max.filesize设定大小为10G。
举例:表定义没有指定MEMSTORE_FLUSHSIZE,hbase.hregion.memstore.flush.size默认128M,hbase.hregion.max.filesize默认10G。 那么第一个region 是memstore刷盘到HFile的大小,即 1*1*128M=128M,生成一个region。 (1*1是region个数的平方) 当region数达到2个时,2*2*128M=512M。 (2*2是region个数的平方) 当region数达到3个时,3*3*128M=1152M。 (3*3是region个数的平方) ...依此类推... 当region数达到9个时,9*9*128M= 10368M > 10G。从第10个region开始每个region最大大小为10G(hbase.hregion.max.filesize)
同时还有一种策略是KeyPrefixRegionSplitPolicy,通过rowkey前缀位数来划分region,策略适合固定前缀的rowkey。
Qunar方便运维和管理统一使用的ConstantSizeRegionSplitPolicy(什么时候触发region split一目了然)。
Region Split流程
Region Split的流程如下:
1.客户端向RegionServer发送写入请求。 2.数据写入到内存memstore。 3.当memstore被填满并达到阀值,存储在内存中的数据被写入到磁盘生成HFiles,过程叫做memstore flush。 4.随着HFiles的增加,regionserver通过compact来将文件压缩成较大的文件。 5.region的数据量增加。 6.RegionServer根据region split的策略确定所属的region是否应该进行region split。 7.如果需要进行region split,则将region split请求加入到队列中(split_queue)
但是一个region实际是如何进行split呢? 是否会在某一个row key上进行split? split region会做什么?会发生什么?
RegionServer是通过如下流程进行region拆分:
1.当RegionServer确定需要对某个region进行拆分操作时,它将启动一个拆分事务。RegionServer首先会在表上获取一个共享锁 来防止拆分期间的schema修改。之后会在ZooKeeper中 /hbase/region-in-transition/region-name节点下创建一个znode,并将 znode状态设置为SPLITTING。 HMaster监视znode /hbase/region-in-transition时发现region(父region)需要开始拆分。
2.RegionServer在HDFS的region目录中创建一个名为.splits子目录,关闭父region并下线要拆分的父Region(本地数据结构标记 为下线)。客户端发送到给拆分region的请求都会获取到NotServingRegionException异常触发重试,该Region(父region)不再接受任何请求。
3.RegionServer在 .splits目录下(多线程并行)创建两个子region目录并创建必要的数据结构。
4.RegionServer创建两个引用文件并且引用文件指向父region的文件。
5.RegionServer在HDFS中真正创建子region目录(后续online),并将引用文件传送到相应的子region目录。
6.RegionServer向hbase:meta表发送put请求,将新建的两个子region和rowkey范围写入meta表并将父region标记为offline。在信息 没有加入到meta表之前,Client能查找到的只是父region,并不会查到新建的两个子region,同时子region在zk中的状态为SPLITTING_NEW。 如果数据写入hbase:meta成功,则region已经有效的完成拆分。如果RegionServer在单次RPC成功完成之前返回失败,则Master和 下一个RegionServer会重新打开该父region将清除有关该region拆分的脏状态和数据。如果hbase:meta更新成功完成,HBase则继续 region split的流程。如果拆分失败,则父region状态从SPLITTING变为OPEN,并且两个子region的状态从SPLITTING_NEW变为OFFLINE。
7.RegionServer并行的打开两个子region。
8.RegionServer将新region添加到hbase:meta并online。
9.之后客户端的相关请求会定向到新region并发送请求,客户端hbase:meta历史缓存被清除(因为父region已经下线,缓存非最新数据) 并从hbase:meta获取新region的信息并缓存。
10.RegionServer更新zk中znode /hbase/region-in-transition/region-name为split状态,新region的状态从SPLITTING_NEW为OPEN。 HMaster从znode发现更新,如果此时需要进行负载均衡,Hmaster会将两个子region重新分配给其它regionserver。整个region split过程完成。 对hbase中父reigon的引用会在hbase:meta和HDFS中通过compact来删除。删除过程中HMaster会定期检查子region是否还有引用指向 父region,如果没有指向,则将父region删除。
Tips:region split需要获取表锁,拆分过程中所有步骤依赖于zk,通过znode的状态进行跟踪,保证当split过程中出现问题时其它的进程能直到region的状态。
Compact
HBase合并一直是运维过程中的重点,这里打算简单说下。后续有时间整理单独的文章进行细节性总结,不是懒,是因为太多了(手动捂脸)。
HBase的写入流程是优先写WAL来实现数据不丢失,然后数据写入内存中的memstore,当memstore中数据达到给定的阀值后触发刷盘生成HFile。通过滚动memstore将数据移出内存同时丢弃对应的提交日志,内存中只保存数据为持久化道磁盘的日志。(滚动memstore是空的新memstore获取数据写入,满的memstore转存为HFile文件后移除内存)。
HBase的写入流程随着memstore中的数据不断刷写到磁盘,产生的HFile文件越来越多,HBase采用合并的方式来解决该问题。合并是指将多个文件合并成一个较大的文件。合并类型有:小合并 minor compaction 和大合并 major compaction。
小合并(minor compaction)是将多个小文件重写为数量较少的大文件,减少存储文件的数量。算法是多路合并,因为HFile文件本身是有序(memstore刷hfile前会在内存排序)的并经过归类的,所以合并速度很快,取决于磁盘IO的能力。设置minor合并处理最小文件数量由参数hbase.hstore.compaction.min设置,默认值是3。该属性最小值需要大于或等于2,过大的数字将会延迟minor合并的执行,同时也会增加执行时消耗的资源和执行时间。设置minor合并处理最大文件数量由参数hbase.hstore.compaction.max设置,默认值是10。设置合并文件大小限制是由参数hbase.hstore.compaction.max.size设置,默认值是Long.Max_VALUE,任何大于该参数值的HFile都不会参与合并。
大合并(major compaction)是将一个region中一个列族的若干个HFile重写为一个新HFile,major合并负责把所有文件压缩成一个单独的文件,在执行压缩检查时,系统自动决定运行哪种合并。
大合并和小合并不同的是:大合并能扫描所有的键值对,顺序重写全部的数据,重写数据的过程会跳过删除标记的数据,对于那些超过版本号限制和生存时间到期的数据,在重写数据时同样不会重写入新HFile。
合并和拆分在运维过程中需要考虑IO风暴的问题,避免磁盘IO压力。原因是如果HBase region按照预定的速度进行增长,触发region split的时间可能在同一时间进行,region split本身会有IO开销,当同时进行major compact时则会引起IO风暴,因此运维过程中建议手动进行split和手动进行compact。手动split的方法是将hbase.hregion.max.filesize值设置得很大(例如100G,不会经常或触发不了split),手动compact则是设置参数hbase.hregion.majorcompaction为0,凌晨通过cron定时运行大合并。
通过手动管理split和major compact能避免操作在不同的region上错开执行,避免IO集中,降低IO负载来避免拆分/合并风暴。
注意目前hbase flush和compaction操作是针对一个Region。其中Compaction操作是根据一个列族下的全部文件的数量触发的,而不是根据文件大小触发的。当一个列族操作大量数据的时候会引发一个flush,如果有多个列族则会将那些不相关的列族也进行flush操作。当很多的列族在flush和compaction时,会造成很多没用的I/O负载,要想解决这个问题需要将flush和compaction操作只针对一个列族,即创建表时指定一个列族 。
针对IO瓶颈可以适当考虑使用SSD作为存储,例如我们某个业务的统计分析集群就是使用Shannon SSD进行加速满足性能需求。综合考虑业务场景(例如历史订单和实时推荐系统)和成本,HBase可以考虑使用SSD进行性能加速,HBase自身由于数据读取多种路径(优先在memstore读取最新写入数据其次在blockcache读取缓存数据最后从磁盘HFile文件加载到内存读取数据),同时原生HBase不支持二级索引导致要么通过rowkey查询要么全表扫描,查询性能通常比RDBMS差一些(当然HBase读延时也有很多优化手段,有时间可以总结),如果将热点数据能够全部加载到block cache(RAM/SSD)或者通过SSD加速HFile读取,HBase读写性能将大大提升,另外BucketCache file模式同样是建议使用SSD存储加速性能。官方同样对SSD做了相关的优化,详情见 。
HBase hbck
hbck是HBase的管理用具,用于检查或修复HBase,当HBase集群中出现不一致的地方时hbck会格式化输出。hbck经常用于检查region一致性和表完整性。hbck工具运行会扫描整个.META表来收集表相关信息,会访问所有RegionServer以及读取所有table region里的regioninfo,所以hbck操作很重并且需要获取表锁。 同时运维过程中需要知道hbck 报告有可能是暂时的,例如region split或某个时间不可用时hbck也会打印这些不一致信息。可以加上-details参数获取详细信息并多次运行hbck确定问题是否是暂时的。
常用例子
修复region一致性:hbase hbck -fixAssignments
修复region在HDFS和META信息不一致: hbase hbck -fixAssignments -fixMeta
修复 region空洞或起止rowkey重叠问题: hbase hbck -repairHoles 或 hbase hbck -repair
META没有被分配,则将其正确分配: hbase hbck -fixMetaOnly -fixAssignments
META表损坏: hbase org.apache.hadoop.hbase.util.hbck.OfflineMetaRepair
离线父region发生split: hbase hbck -fixSplitParents
HBase版本文件丢失: hbase hbck -fixVersionFile