region
过多
181
分裂过度
首先是分裂功能的误用。
HBase
的“表详细信息”
Web
后台允许管理员将特定表的
所有
region
分裂。只要
region
中的数据量满足手动分裂的最小尺寸,
HBase
就可以不
断地将
region
分裂。这个命令选项有时候非常有用,但是一旦滥用,会造成某一表
region
过多。举例而言,一个表有
128
region
,分布在一个四节点集群上,每一
RegionServer
服务器平均拥有
32
region
,这是这种大小的集群能承受的正常负
载,运用分裂选项三次,将产生
1024
region
,每一台平均
256
region!
预分裂不合理
HBase
的预分裂功能同样可能造成
region
过多。预分裂能使负载分散到集群中的所
RegionServer
上,有着非常重要的作用,但是使用它一定要事先深思熟虑。应该
预先了解基本的
HBase
行键设计、写操作,以及正确将表预分裂所需的
region
数。预
分裂不当,会造成
region
过多(成百上千个),但仅少数
region
能被用到。
解决方案
region
过多的问题有不同的方法可以解决,具体取决于你所用的
HBase
版本。但无论
是何种情境,最终的目标都是合并一些
region
,降低
region
总数,不同
HBase
版本之
间的差别在于实现这个目标的方法。
0.98
之前的版本
0.98
版本之前,合并
region
有两种主要的方法:拷贝一个表到一个新的预分裂的
表,或者直接合并
region
。在
0.98
版本之前,
region
只能离线时候合并。
第一种方法是创建一个和已存在表的定义相同、但是表名不同的新表。将原来那个
表的数据拷贝到新表中。完成后,丢开掉原来的表,以旧表表名重命名新表。这个
方法需要用
HBase
的快照功能(
HBase0.94.6
版本以上)。同样你需要在
HBase
集群
至上运行
MapReduce
的拷贝表的任务,所以需要确保
MapReduce
框架是否可用或者
能运行。最后,这个方法需要暂停数据写入,这个中断过程是短暂的,但是是整个
过程重要的一部分。
因为这个方法要复制原表,会消耗和原表一样大的集群空间,所在开始前要确
保有多余的空间可用。了解运行
MapReduce
HBase
集群的影响也很重要。
182
14
新表应该按照期望的新合并
region
之间的边界进行预分裂,如图
14-2
所示。一旦预分
裂完成,便用
CopyTable
命令从原表拷贝数据到新表中。
CopyTable
工具以一个日期
范围作为参数,然后将时间
t0
t1
的数据直接拷贝到目标表,拷入正确的
region
中。
这使得你可以先传输旧数据,然后重新运行工具,再传输新数据。
CopyTable
同样允
许你重命名或者丢弃不必要的列族,这个在表重设计的时候很有用。
如图
14-1
所示,如果你用修改过的时间戳进行
puts
deletes
操作,你应该避免
使用这个方法,这是因为,当在两个
CopyTable
调用之间的来源侧出现任何合
并的话,部分
deletes
puts
操作可能无法正确复制。修改内部
HBase
的时间戳绝
不是什么好主意。在图
14-1
中,在
t0
t1
之间完成了
CopyTable
,把数据从一个
表拷贝到另一个表。当在
t1
到当前时刻之间运行
CopyTable
拷贝数据时,一些印
有旧时间戳的数据会插入到源表中。由于只有时间戳大于
t1
的数据才会在第二
次运行
CopyTable
纳入考虑,
t1
之前新插入的数据将丢失掉(比如,这些数据不
会被拷贝到新表,当源表被丢弃时,数据则将永久丢失)。
在t0到t1之间完成了CopyTable
普通的插入操作 用了t1之前的时间戳做的插入操作
在t1到t2之间完成了CopyTable
在之前时间戳更新的单元格
不被拷贝丢失信息
14
-
1
:在按单元格旧时间戳更新时存在的风险
region
过多
183
14-2
的示例能更好的理解这个过程。假设你创建了一个应用,以事务
ID
作为行键
将事务存储到一个表中。你最初的假设是,事务
ID
是一个随机产生的可读字符串,
独一无二。基于这个假设,初始表可以被分成
A
Z
26
region
。但是当应用运行
几周以后,你发现事务
ID
是十六进制的,因此,它是从
0
F
而不是从
A
Z
。因于
这是一个十六进制的分布,大于
F
的分区便从来得不到利用,而第一个分区(存放
0
A
的数据)获得远比其他分区更多的数据,并随着数据增长将分裂成诸多新
region
。这种情况下,我们把
26
个分区的数据仅移动到
8
个分区中。但是如果扩展此
示例,你会看到成百上千的
region
最初是如何为了处理大的负载而创建的,以及同
样的数据平衡问题是如何发生的。
原始表 新表
14
-
2
region重设计
为了解决这个问题,我们建一个新表,按照十六进制预分裂。使用
CopyTable
命令,
将数据从现有的表复制到新表,从
t0
到当前日期:
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --starttime=0 \
--endtime=1411264757000 --new.name=new_table previous_table
使用
starttime
endtime
参数的目的是确保我们拷贝的是源表中特定的可控区域
段。因为这个操作可能会花一些时间,所在在进行下一步之前,我们要尽可能地拷
贝足够多的数据,减少表的停机时间,但是我们也要确保能从一个特定的时间点重
新启动拷贝操作。
184
14
拷贝操作可能花费几个小时到数天,具体取决于数据集的大小。当拷贝在运行的时
候,普通的操作可以继续在这个集群或者初始表上执行。
一旦第一个
CopyTable
操作完成,新表除了你先前操作开始(在本例中,
t=141126475700
)到现在的数据没有外,其他数据都包含了。具体缺少多少数据,
取决你第一个操作命令运行了多久,很有可能是不少的数据。有鉴于此,为了确保
下一步命令执行的时间越短越好,我们要重新执行
CopyTable
命令。由于在先前的命
令中使用的
endtime
参数是独一无二的,我们就用它作为当前的
starttime
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --starttime=1411264757000 \
--endtime=1411430414000 --new.name=new_table previous_table
第二个操作应该会比较快,因为大部分的数据在第一次的时候便复制完毕。当两个
CopyTable
都完成后,在进行最后一次运行之前,为确保数据不丢失,在执行后面
命令的时候,需要停止将数据写到现在的表中。在生产环境中,有时候停止写数据
是非常敏感的,如果是这种情况,你可以考虑一些缓解方案。其中之一是把数据写
到另一个新建的表中;也可以设计能容忍这种短暂停顿的写操作,像
Kafka
Flume
等应用被用来做队列缓冲向
HBase
写入的数据,能容忍短暂的停顿而不会丢任何数
据。通常,剩下的步骤在不到一小时完成,但是很难量化真正花多少时间。可以凭
以前的运行计算,但是可能会相差很大。
最后,需要最后运行一次
CopyTable
工具拷贝剩下的数据。从上一次运行命令的
设置的结束时间到你停止数据写入的时刻不过几分钟,所以这次会运行得很快。
这次,我们想要拷贝所有剩下的数据,我们再次用上一次的
endtime
值作为这次的
starttime
,但是不指定具体的
endtime
hbase org.apache.hadoop.hbase.mapreduce.CopyTable --starttime=1411264757000 \
--new.name=new_table previous_table
这个可能运行几秒钟到几分钟。在这个操作结束时,我们完整的复制了源表的数据
到新分区的新表中。最后一步是运行几条快照命令重命名新建的表:
disable 'events_table'
drop 'events_table'
disable 'new_events_table'
snapshot 'new_events_table', 'snap1'
clone_snapshot 'snap1', 'events_table'
delete_snapshot 'snap1'
drop 'new_events_table'
完成这些操作后,表中包含的数据相同、但是少了不少
region
region
过多
185
离线的
merge
减少
regions
数量的第二种选择是执行离线
merge
。为了能够执行后面的步骤,需要
运行
0.94.13
或者更高版本的
HBase
0.94.13
之前的版本进行离线
merge
可能会失败或
者在你系统中留下一堆损坏的文件(参考
HBase-9504
)。离线
merge
受到的主要约
束是它们需要集群的总共停机时间。
执行离线的
merge
有以下几步(稍后我们会详细研究):
1.
停止集群。
2.
执行一个或者多个
merge
操作。
3.
启动集群。
停止和开始
HBase
集群,可以用
bin/stop-hbase.sh
bin/starthbase.sh
命令。如果你使
用像
Cloudera Manager
这样的管理工具,在
HBase
服务管理页面可以看到
HBase
的这
些命令。
使用
HBase
命令
用下面的命令中
HBase
Merge
类执行离线的
merge
bin/hbase org.apache.hadoop.hbase.util.Merge testtable \
"testtable,,1411948096799.77873c05283fe40822ba69a30b601959."\
"testtable,11111111,1411948096800.e7e93a3545d36546ab8323622e56fdc4."
这个命令有三个参数:第一个是表名,另两个是要合并的
region
。你可以从
HBase
后台的网页上获得
region
的名字。当然,具体取决于键的设计,从命令行运行很可
能非常困难。你的键可能包含了一些保留字符(如
$
^
等),会导致格式化有点困
难。如果是这种情况,则不能格式化命令,那么使用下文介绍的
Java API
方法可能
比较明知。一旦执行命令,
HBase
会首先测试集群是否停止了,然后会执行多个命
令去合并这两个
region
的内容到一个
region
中。这个过程同样会创建一具拥有新的边
界的
region
,创建相关的目录结构,将内容移至新的
region
中,更新
HBase .META.
信息。操作的持续时间取决于你合并的
region
大小。
使用
Java API
使用
Java API
可以执行同样的操作,我们充分利用
HBase
merge
工具类,在给出正
确的参数时候调用它。这个示例专门针对
HBase 0.94
0.96
,不能使用
HBase
后面
的版本编译。这里省略了部分代码,仅在例
14-1
中提供了一小段代码。

Get HBase应用架构 now with O’Reilly online learning.

O’Reilly members experience live online training, plus books, videos, and digital content from 200+ publishers.