内容简介:HBase中表的基本单位是Region,日常在调用HBase API操作一个表时,交互的数据也会以Region的形式进行呈现。一个表可以有若干个Region,今天笔者就来和大家分享一下Region合并的一些问题和解决方法。在分析合并Region之前,我们先来了解一下Region的体系结构,如下图所示:
1.概述
HBase中表的基本单位是Region,日常在调用HBase API操作一个表时,交互的数据也会以Region的形式进行呈现。一个表可以有若干个Region,今天笔者就来和大家分享一下Region合并的一些问题和解决方法。
2.内容
在分析合并Region之前,我们先来了解一下Region的体系结构,如下图所示:
从图中可知,能够总结以下知识点:
-
HRegion:一个Region可以包含多个Store;
-
Store:每个Store包含一个Memstore和若干个StoreFile;
-
StoreFile:表数据真实存储的地方,HFile是表数据在HDFS上的文件格式。
如果要查看HFile文件,HBase有提供命令,命令如下:
1hbase hfile -p -f /hbase/data/default/ip_login/d0d7d881bb802592c09d305e47ae70a5/_d/7ec738167e9f4d4386316e5e702c8d3d
执行输出结果,如下图所示:
2.1 为什么需要合并Region
那为什么需要合并Region呢?这个需要从Region的Split来说。当一个Region被不断的写数据,达到Region的Split的阀值时(由属性hbase.hregion.max.filesize来决定,默认是10GB),该Region就会被Split成2个新的Region。随着业务数据量的不断增加,Region不断的执行Split,那么Region的个数也会越来越多。
一个业务表的Region越多,在进行读写操作时,或是对该表执行Compaction操作时,此时集群的压力是很大的。这里笔者做过一个线上统计,在一个业务表的Region个数达到9000+时,每次对该表进行Compaction操作时,集群的负载便会加重。而间接的也会影响应用程序的读写,一个表的Region过大,势必整个集群的Region个数也会增加,负载均衡后,每个RegionServer承担的Region个数也会增加。
因此,这种情况是很有必要的进行Region合并的。比如,当前Region进行Split的阀值设置为30GB,那么我们可以对小于等于10GB的Region进行一次合并,减少每个业务表的Region,从而降低整个集群的Region,减缓每个RegionServer上的Region压力。
2.2 如何进行Region合并
那么我们如何进行Region合并呢?HBase有提供一个合并Region的命令,具体操作如下:
1# 合并相邻的两个Region 2hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME' 3# 强制合并两个Region 4hbase> merge_region 'ENCODED_REGIONNAME', 'ENCODED_REGIONNAME', true
但是,这种方式会有一个问题,就是只能一次合并2个Region,如果这里有几千个Region需要合并,这种方式是不可取的。
2.2.1 批量合并
这里有一种批量合并的方式,就是通过编写脚本(merge_small_regions.rb)来实现,实现代码如下:
1# Test Mode:
2#
3# hbase org.jruby.Main merge_empty_regions.rb namespace.tablename <skip_size> <batch_regions> <merge?>
4#
5# Non Test - ie actually do the merge:
6#
7# hbase org.jruby.Main merge_empty_regions.rb namespace.tablename <skip_size> <batch_regions> merge
8#
9# Note: Please replace namespace.tablename with your namespace and table, eg NS1.MyTable. This value is case sensitive.
10
11require 'digest'
12require 'java'
13java_import org.apache.hadoop.hbase.HBaseConfiguration
14java_import org.apache.hadoop.hbase.client.HBaseAdmin
15java_import org.apache.hadoop.hbase.TableName
16java_import org.apache.hadoop.hbase.HRegionInfo;
17java_import org.apache.hadoop.hbase.client.Connection
18java_import org.apache.hadoop.hbase.client.ConnectionFactory
19java_import org.apache.hadoop.hbase.client.Table
20java_import org.apache.hadoop.hbase.util.Bytes
21
22def list_bigger_regions(admin, table, low_size)
23 cluster_status = admin.getClusterStatus()
24 master = cluster_status.getMaster()
25 biggers = []
26 cluster_status.getServers.each do |s|
27 cluster_status.getLoad(s).getRegionsLoad.each do |r|
28 # getRegionsLoad returns an array of arrays, where each array
29 # is 2 elements
30
31 # Filter out any regions that don't match the requested
32 # tablename
33 next unless r[1].get_name_as_string =~ /#{table}\,/
34 if r[1].getStorefileSizeMB() > low_size
35 if r[1].get_name_as_string =~ /\.([^\.]+)\.$/
36 biggers.push $1
37 else
38 raise "Failed to get the encoded name for #{r[1].get_name_as_string}"
39 end
40 end
41 end
42 end
43 biggers
44end
45
46# Handle command line parameters
47table_name = ARGV[0]
48low_size = 1024
49if ARGV[1].to_i >= low_size
50 low_size=ARGV[1].to_i
51end
52
53limit_batch = 1000
54if ARGV[2].to_i <= limit_batch
55 limit_batch = ARGV[2].to_i
56end
57do_merge = false
58if ARGV[3] == 'merge'
59 do_merge = true
60end
61
62config = HBaseConfiguration.create();
63connection = ConnectionFactory.createConnection(config);
64admin = HBaseAdmin.new(connection);
65
66bigger_regions = list_bigger_regions(admin, table_name, low_size)
67regions = admin.getTableRegions(Bytes.toBytes(table_name));
68
69puts "Total Table Regions: #{regions.length}"
70puts "Total bigger regions: #{bigger_regions.length}"
71
72filtered_regions = regions.reject do |r|
73 bigger_regions.include?(r.get_encoded_name)
74end
75
76puts "Total regions to consider for Merge: #{filtered_regions.length}"
77
78filtered_regions_limit = filtered_regions
79
80if filtered_regions.length < 2
81 puts "There are not enough regions to merge"
82 filtered_regions_limit = filtered_regions
83end
84
85if filtered_regions.length > limit_batch
86 filtered_regions_limit = filtered_regions[0,limit_batch]
87 puts "But we will merge : #{filtered_regions_limit.length} regions because limit in parameter!"
88end
89
90
91r1, r2 = nil
92filtered_regions_limit.each do |r|
93 if r1.nil?
94 r1 = r
95 next
96 end
97 if r2.nil?
98 r2 = r
99 end
100 # Skip any region that is a split region
101 if r1.is_split()
102 r1 = r2
103 r2 = nil
104 puts "Skip #{r1.get_encoded_name} bcause it in spliting!"
105 next
106 end
107 if r2.is_split()
108 r2 = nil
109 puts "Skip #{r2.get_encoded_name} bcause it in spliting!"
110 next
111 end
112 if HRegionInfo.are_adjacent(r1, r2)
113 # only merge regions that are adjacent
114 puts "#{r1.get_encoded_name} is adjacent to #{r2.get_encoded_name}"
115 if do_merge
116 admin.mergeRegions(r1.getEncodedNameAsBytes, r2.getEncodedNameAsBytes, false)
117 puts "Successfully Merged #{r1.get_encoded_name} with #{r2.get_encoded_name}"
118 sleep 2
119 end
120 r1, r2 = nil
121 else
122 puts "Regions are not adjacent, so drop the first one and with the #{r2.get_encoded_name} to iterate again"
123 r1 = r2
124 r2 = nil
125 end
126end
127admin.close
该脚本默认是合并1GB以内的Region,个数为1000个。如果我们要合并小于10GB,个数在4000以内,脚本(merging-region.sh)如下:
1#! /bin/bash 2 3num=$1 4 5echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : RegionServer Start Merging..." 6if [ ! -n "$num" ]; then 7 echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : Default Merging 10 Times." 8 num=10 9elif [[ $num == *[!0-9]* ]]; then 10 echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : Input [$num] Times Must Be Number." 11 exit 1 12else 13 echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : User-Defined Merging [$num] Times." 14fi 15 16for (( i=1; i<=$num; i++ )) 17do 18 echo "[`date "+%Y-%m-%d %H:%M:%S"`] INFO : Merging [$i] Times,Total [$num] Times." 19 hbase org.jruby.Main merge_small_regions.rb namespace.tablename 10240 4000 merge 20 sleep 5 21done
在merging-region.sh脚本中,做了参数控制,可以循环来执行批量合并脚本。可能在实际操作过程中,批量执行一次Region合并,合并后的结果Region还是有很多(可能此时又有新的Region生成),这是我们可以使用merging-region.sh这个脚本多次执行批量合并Region操作,具体操作命令如下:
1# 默认循环10次,例如本次循环执行5次 2sh merging-region.sh 5
2.3 如果在合并Region的过程中出现永久RIT怎么办
在合并Region的过程中出现永久RIT怎么办?笔者在生产环境中就遇到过这种情况,在批量合并Region的过程中,出现了永久MERGING_NEW的情况,虽然这种情况不会影响现有集群的正常的服务能力,但是如果集群有某个节点发生重启,那么可能此时该RegionServer上的Region是没法均衡的。因为在RIT状态时,HBase是不会执行Region负载均衡的,即使手动执行balancer命令也是无效的。
如果不解决这种RIT情况,那么后续有HBase节点相继重启,这样会导致整个集群的Region验证不均衡,这是很致命的,对集群的性能将会影响很大。经过查询HBase JIRA单,发现这种MERGING_NEW永久RIT的情况是触发了HBASE-17682的BUG,需要打上该Patch来修复这个BUG,其实就是HBase源代码在判断业务逻辑时,没有对MERGING_NEW这种状态进行判断,直接进入到else流程中了。源代码如下:
1for (RegionState state : regionsInTransition.values()) {
2 HRegionInfo hri = state.getRegion();
3 if (assignedRegions.contains(hri)) {
4 // Region is open on this region server, but in transition.
5 // This region must be moving away from this server, or splitting/merging.
6 // SSH will handle it, either skip assigning, or re-assign.
7 LOG.info("Transitioning " + state + " will be handled by ServerCrashProcedure for " + sn);
8 } else if (sn.equals(state.getServerName())) {
9 // Region is in transition on this region server, and this
10 // region is not open on this server. So the region must be
11 // moving to this server from another one (i.e. opening or
12 // pending open on this server, was open on another one.
13 // Offline state is also kind of pending open if the region is in
14 // transition. The region could be in failed_close state too if we have
15 // tried several times to open it while this region server is not reachable)
16 if (state.isPendingOpenOrOpening() || state.isFailedClose() || state.isOffline()) {
17 LOG.info("Found region in " + state +
18 " to be reassigned by ServerCrashProcedure for " + sn);
19 rits.add(hri);
20 } else if(state.isSplittingNew()) {
21 regionsToCleanIfNoMetaEntry.add(state.getRegion());
22 } else {
23 LOG.warn("THIS SHOULD NOT HAPPEN: unexpected " + state);
24 }
25 }
26 }
修复之后的代码如下:
1for (RegionState state : regionsInTransition.values()) {
2 HRegionInfo hri = state.getRegion();
3 if (assignedRegions.contains(hri)) {
4 // Region is open on this region server, but in transition.
5 // This region must be moving away from this server, or splitting/merging.
6 // SSH will handle it, either skip assigning, or re-assign.
7 LOG.info("Transitioning " + state + " will be handled by ServerCrashProcedure for " + sn);
8 } else if (sn.equals(state.getServerName())) {
9 // Region is in transition on this region server, and this
10 // region is not open on this server. So the region must be
11 // moving to this server from another one (i.e. opening or
12 // pending open on this server, was open on another one.
13 // Offline state is also kind of pending open if the region is in
14 // transition. The region could be in failed_close state too if we have
15 // tried several times to open it while this region server is not reachable)
16 if (state.isPendingOpenOrOpening() || state.isFailedClose() || state.isOffline()) {
17 LOG.info("Found region in " + state +
18 " to be reassigned by ServerCrashProcedure for " + sn);
19 rits.add(hri);
20 } else if(state.isSplittingNew()) {
21 regionsToCleanIfNoMetaEntry.add(state.getRegion());
22 } else if (isOneOfStates(state, State.SPLITTING_NEW, State.MERGING_NEW)) {
23 regionsToCleanIfNoMetaEntry.add(state.getRegion());
24 }else {
25 LOG.warn("THIS SHOULD NOT HAPPEN: unexpected " + state);
26 }
27 }
28 }
但是,这里有一个问题,目前该JIRA单只是说了需要去修复BUG,打Patch。但是,实际生产情况下,面对这种RIT情况,是不可能长时间停止集群,影响应用程序读写的。那么,有没有临时的解决办法,先临时解决当前的MERGING_NEW这种永久RIT,之后在进行HBase版本升级操作。
办法是有的,在分析了MERGE合并的流程之后,发现HBase在执行Region合并时,会先生成一个初始状态的MERGING_NEW。整个Region合并流程如下:
从流程图中可以看到,MERGING_NEW是一个初始化状态,在Master的内存中,而处于Backup状态的Master内存中是没有这个新Region的MERGING_NEW状态的,那么可以通过对HBase的Master进行一个主备切换,来临时消除这个永久RIT状态。而HBase是一个高可用的集群,进行主备切换时对用户应用来说是无感操作。因此,面对MERGING_NEW状态的永久RIT可以使用对HBase进行主备切换的方式来做一个临时处理方案。之后,我们在对HBase进行修复BUG,打Patch进行版本升级。
3.总结
HBase的RIT问题,是一个比较常见的问题,在遇到这种问题时,可以先冷静的分析原因,例如查看Master的日志、仔细阅读HBase Web页面RIT异常的描述、使用hbck命令查看Region、使用fsck查看HDFS的block等。分析出具体的原因后,我们在对症下药,做到大胆猜想,小心求证。
4.结束语
长按关注,获取更多干货
这篇博客就和大家分享到这里,如果大家在研究学习的过程当中有什么问题,可以加群进行讨论或发送邮件给我,我会尽我所能为您解答,与君共勉!
博主出版了《Hadoop大数据挖掘从入门到进阶实战》,喜欢的朋友或同学, 可以去购买博主的书进行学习,在此感谢大家的支持。
以上所述就是小编给大家介绍的《HBase Region 合并分析》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 数据分析之Pandas合并操作总结
- 不容错过的Pandas小技巧:万能转格式、轻松合并、压缩数据,让数据分析更高效
- 分组字符合并SQL语句 按某字段合并字符串之一(简单合并)
- git 的合并原理(递归三路合并算法)
- git 合并策略
- 算法 - 合并若干有序文件
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The NSHipster Fake Book (Objective-C)
Mattt Thompson / NSHipster Portland, Oregon / 2014 / USD 19.00
Fake Books are an indispensable tool for jazz musicians. They contain the melody, rhythm, and chord changes for hundreds of standards, allowing a player to jump into any session cold, and "fake it" th......一起来看看 《The NSHipster Fake Book (Objective-C)》 这本书的介绍吧!