hbase
文章目录
- 1.简介
- 2.HBase Shell基本操作
- 2.1 HBase shell
- 2.1.1 命令举例
- 3.HBase特点与使用场景
- 3.1 HBase特点
- 3.2 使用场景
- 4. HBase基本组件分析
- 4.1 HBase HMaster
- 4.2 ZooKeeper: The Coordinator
- 4.3 ReginServer
- 4.4 Client
- 5.存储
- 5.1存储
- 5.2 预分区
- 6.RegionServer的主要流程
- 6.1 首次读写流程
- 6.2 写流程
- 6.3 再次读流程
- 6.4 WAL Splitting 流程
- 7.5 WAL Repaly 流程
- 7.高级特性
- 7.1 coprocessor[实验]
- 7.1.1 Coprocessor种类
- 7.1.2 Coprocessor使用流程
- 7.1.3 HBase动态加载jar包步骤
- 7.2 WAL[恢复]
- 7.3 高可用性
- 7.4 结合mapreduce操作HBase[实验]
- 7.4.1 使用MapReduce做全表扫描
- 7.4.2 BulkLoad
- 8.使用java API操作HBase
- 8.1 环境配置
- 8.2示例程序一:创建表
- 8.3示例程序二:操作表
- 8.4示例程序四:自定义配置
- 9.常见问题与疑惑
1.简介
HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”。就像Bigtable利用了Google文件系统(File System)所提供的分布式数据存储一样,HBase在Hadoop之上提供了类似于Bigtable的能力。HBase是Apache的Hadoop项目的子项目。HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式。
传统的行存储和列存储的区别可从下图看出:
1.1数据模型
在 HBase 的数据被存储在表中,具有行和列。这和关系数据库(RDBMS中)的术语是重叠,但在概念上它们不是一类。相反,应该将 HBase 的表当作是一个多维的 map 结构而更容易让人理解。
1.2 术语
- Table(表):HBase table 由多个 row 组成。
- Row(行):每一 row 代表着一个数据对象,每一 row 都是以一个 row key(行键)和一个或者多个 column 组成。row key 是每个数据对象的唯一标识的,按字母顺序排序,即 row 也是按照这个顺序来进行存储的。所以,row key 的设计相当重要,一个重要的原则是,相关的 row 要存储在接近的位置。比如网站的域名,row key 就是域名,在设计时要将域名反转(例如,org.apache.www、org.apache.mail、org.apache.jira),这样的话, Apache 相关的域名在 table 中存储的位置就会非常接近的。
- Column(列):column 由 column family 和 column qualifier 组成,由冒号(:)进行进行间隔。比如family:qualifier。
- Column Family(列族):在 HBase,column family 是 一些 column 的集合。一个 column family 所有 column 成员是有着相同的前缀。比如, courses:history 和 courses:math 都是 courses 的成员。冒号(:)是 column family 的分隔符,用来区分前缀和列名。column 前缀必须是可打印的字符,剩下的部分列名可以是任意字节数组。column family 必须在 table 建立的时候声明。column 随时可以新建。在物理上,一个的 column family 成员在文件系统上都是存储在一起。因为存储优化都是针对 column family 级别的,这就意味着,一个 column family 的所有成员的是用相同的方式访问的。
- Column Qualifier(列限定符):column family 中的数据通过 column qualifier 来进行映射。column qualifier 也没有特定的数据类型,以二进制字节来存储。比如某个 column family “content”,其 column qualifier 可以设置为 “content:html” 和 “content:pdf”。虽然 column family 是在 table 创建时就固定了,但 column qualifier 是可变的,可能在不同的 row 之间有很大不同。
- Cell(单元格):cell 是 row、column family 和 column qualifier 的组合,包含了一个值和一个 timestamp,用于标识值的版本。
- Timestamp(时间戳):每个值都会有一个 timestamp,作为该值特定版本的标识符。默认情况下,timestamp 代表了当数据被写入 RegionServer 的时间,但你也可以在把数据放到 cell 时指定不同的 timestamp。
1.3概念视图
HBase的概念视图就是一张表。除了表格方式来展现数据视图,也使用使用多维 map,如下:
{
"com.cnn.www": {
contents: {
t6: contents:html: "<html>..."
t5: contents:html: "<html>..."
t3: contents:html: "<html>..."
}
anchor: {
t9: anchor:cnnsi.com = "CNN"
t8: anchor:my.look.ca = "CNN.com"
}
people: {}
}
"com.example.www": {
contents: {
t5: contents:html: "<html>..."
}
anchor: {}
people: {
t5: people:author: "John Doe"
}
}
}
1.4物理视图
在概念视图里,table 可以被看成是一个稀疏的== row 的集合==。但在物理上,它的是按照 column family 存储的。新的 column qualifier (column_family:column_qualifier)可以随时添加进已有的 column family 。
2.HBase Shell基本操作
针对HBase的操作主要有两种:1,通过HBase shell操作HBase;2,通过HBase Java API进行操作。
2.1 HBase shell
HBase shell常用命令如下:
名称 | 命令表达式 |
---|---|
创建表 | create ‘表名称’, ‘列名称1’,‘列名称2’,‘列名称N’ |
添加记录 | put ‘表名称’, ‘行名称’, ‘列名称:’, ‘值’ |
查看记录 | get ‘表名称’, ‘行名称’ |
查看表中的记录总数 | count ‘表名称’ |
删除记录 | delete ‘表名’ ,‘行名称’ , ‘列名称’ |
删除一张表 | 先要屏蔽该表,才能对该表进行删除,第一步 disable ‘表名称’ 第二步 drop ‘表名称’ |
查看所有记录 | scan “表名称” |
查看某个表某个列中所有数据 | scan “表名称” , [‘列名称:’] |
更新记录 | 就是重写一遍进行覆盖 |
2.1.1 命令举例
首先使用hbase shell命令打开hbase shell,进入交互模式。
(1)查询服务器状态:Status
hbase(main):001:0> status
1 active master, 0 backup masters, 1 servers, 0 dead, 4.0000 average load
hbase(main):002:0> [root@node1 ~]#
(2)创建一个表:create
hbase(main):001:0> create 'table1','col1-familly','col2-familly'
0 row(s) in 4.9710 seconds
(3)列出所有的表:list
hbase(main):004:0> list
TABLE
table
table1
2 row(s) in 0.0140 seconds
(4)插入:put
hbase(main):005:0> put 'table1', 'row1','col1-familly:col1-flag','testdata1'
0 row(s) in 0.5390 seconds
(5)获取记录:get
hbase(main):008:0> get 'table1','row1'
COLUMN CELL
col1-familly:col1-flag timestamp=1501155683860, value=testdata1
col1-familly:col2-flag timestamp=1501155732851, value=testdata1
2 row(s) in 0.0210 seconds
(6)全表扫描:scan
hbase(main):010:0> scan 'table1'
ROW COLUMN+CELL
row1 column=col1-familly:col1-flag, timestamp=1501155683860, value=testdata1
row1 column=col1-familly:col2-flag, timestamp=1501155732851, value=testdata1
1 row(s) in 0.0490 seconds
3.HBase特点与使用场景
3.1 HBase特点
一句话说明,就是:“sparse,consistent,distributed,muldimensional,sorted map”。详细的特点就是:
(1)TTL、多版本、动态列:HBase的代表性功能,用好这些特性能让一些业务事半功倍。
(2)自动分区能力下的无缝水平扩展: HBase区别于很多单机存储系统的特点,是解决大规模数据量的核心能力。
(3)LSM-Tree的数据格式:保证了实时写吞吐的高效,更提供了高效数据导入能力,是与大数据计算完美结合的核心能力。
3.2 使用场景
当你的数据满足如下特征时,说明你可以使用HBase了,并能享受HBase带来的性能提升。
(1)半结构化或非结构化数据
对于数据结构字段不够确定或杂乱无章很难按一个概念去进行抽取的数据适合用HBase。以上面的例子为例,当业务发展需要存储author的email,phone,address信息时RDBMS需要停机维护,而HBase支持动态增加.
(2)记录非常稀疏
RDBMS的行有多少列是固定的,为null的列浪费了存储空间。而如上文提到的,HBase为null的Column不会被存储,这样既节省了空间又提高了读性能。
(3)多版本数据
如上文提到的根据Row key和Column key定位到的Value可以有任意数量的版本值,因此对于需要存储变动历史记录的数据,用HBase就非常方便了。比如上例中的author的Address是会变动的,业务上一般只需要最新的值,但有时可能需要查询到历史值。
(4)超大数据量
当数据量越来越大,RDBMS数据库撑不住了,就出现了读写分离策略,通过一个Master专门负责写操作,多个Slave负责读操作,服务器成本倍增。随着压力增加,Master撑不住了,这时就要分库了,把关联不大的数据分开部署,一些join查询不能用了,需要借助中间层。随着数据量的进一步增加,一个表的记录越来越大,查询就变得很慢,于是又得搞分表,比如按ID取模分成多个表以减少单个表的记录数。经历过这些事的人都知道过程是多么的折腾。采用HBase就简单了,只需要加机器即可,HBase会自动水平切分扩展,跟Hadoop的无缝集成保障了其数据可靠性(HDFS)和海量数据分析的高性能(MapReduce).
总的来说,针对大数据场景下的数据基础量大、增长快、实时性要求迫切、时效性短、易发散、易产生脏数据等问题,HBase还是有天然地优势的。
4. HBase基本组件分析
4.1 HBase HMaster
Master节点主要作用有:
集群中只有一个HMaster,但是会有backup master,当active master down掉后,其他的backup master会在zookeeper中竞争HMaster的位置,保证集群的高可用性。
4.2 ZooKeeper: The Coordinator
ZooKeeper主要作用:
- 系统中的协调者,维护集群状态,当某个服务down掉后,发送通知。通过选举,保证任何时候,集群中只有一个master,Master与RegionServers 启动时会向ZooKeeper注册
- 存贮所有Region的寻址入口
- 实时监控Region server的上线和下线信息。并实时通知给Master
- 存储HBase的schema和table元数据
- 默认情况下,HBase 管理ZooKeeper 实例,比如, 启动或者停止ZooKeeper
- Zookeeper的引入使得Master不再是单点故障
4.3 ReginServer
主要功能:处理Client的数据读写请求(client读写时,直接和RegionServer交互,绕过HMaster)。当某个Region数据过多时,RegionServer会进行Split操作,分裂Region。
4.4 Client
主要功能:
包含访问HBase的接口,并维护cache来加快对HBase的访问,比如region的位置信息。
5.存储
5.1存储
HBase中的每张表都通过行键按照一定的范围被分割成多个子表(HRegion),默认一个HRegion超过256M就要被分割成两个,由HRegionServer管理,管理哪些HRegion由HMaster分配。
HRegionServer存取一个子表时,会创建一个HRegion对象,然后对表的每个列族(Column Family)创建一个Store实例,每个Store都会有0个或多个StoreFile与之对应,每个StoreFile都会对应一个HFile, HFile就是实际的存储文件。因此,一个HRegion有多少个列族就有多少个Store。另外,每个HRegion还拥有一个MemStore实例。memStore存储在内存中,StoreFile存储在HDFS上。
Region虽然是分布式存储的最小单元,但并不是存储的最小单元。Region由一个或者多个Store组成,每个store保存一个columns family;每个Store又由一个memStore和0至多个StoreFile组成,StoreFile包含HFile;memStore存储在内存中,StoreFile存储在HDFS上。
HBase是基于BigTable的面向列的分布式存储系统,其存储设计是基于Memtable / SSTable设计的,主要分为两部分,一部分为内存中的MemStore (Memtable),另外一部分为磁盘(这里是HDFS)上的HFile (SSTable)。还有就是存储WAL的log,主要实现类为HLog.
本质上MemStore就是一个内存里放着一个保存KEY/VALUE的MAP,当MemStore(默认64MB)写满之后,会开始刷磁盘操作。
Table在HBase是按照Regine存储的。HBase在创建表时(不进行预分区的情况下,默认情况)会为表分配一个初始Region。表的Region信息存在.meta表中,该表信息可通过Zookeeper进行追踪。
(当region split时,相应的行会增加两列,splitA和splitB)
结合上面RegionServer图,讲述HBase中的数据存储细节。在HBase中,每一个col familly对应一个HStore。之后,在进行数据操作时,首先会写MemStore,当MemStore中的数据达到一定的限制时,会刷新到StoreFile中。
具体的region存储管理时,每个col familly对应一个Store,Store由一个memStor 加多个StoreFile组成。在进行表增加时,会先写memStore,当memStore文件大小达到一定大小时,会flush到StoreFile中。当Region中的StoreFile文件过多时,会进行Compact操作,将StoreFile合并。当StoreFile进行flush时,属于同一个Region的memstore会一起flush到磁盘中,(如果某个col familly的memstore比较少,也会进行刷新)
HRegionServer、HRegion、Hmemcache、Hlog、HStore之间的关系,如图所示。
HBase表中的数据与HRegionServer的分布关系,如图所示:
5.2 预分区
在创建HBase表的时候默认一张表只有一个region,所有的put操作都会往这一个region中填充数据,当这个一个region过大时就会进行split。如果在创建HBase的时候就进行预分区则会减少当数据量猛增时由于region split带来的资源消耗。
HBase表的预分区需要紧密结合业务场景来选择分区的key值,每个region都有一个startKey和一个endKey来表示该region存储的rowKey范围。
创建包含预分区表的命令如下:
create 't1', 'cf', SPLITS => ['20150501000000000', '20150515000000000', '20150601000000000']
或:
create 't2', 'cf', SPLITS_FILE => '/home/hadoop/splitfile.txt' /home/hadoop/splitfile.txt中存储内容如下: 20150501000000000 20150515000000000 20150601000000000
6.RegionServer的主要流程
6.1 首次读写流程
(1)Client从ZooKeeper中读取hbase:meta表
(2)Clinet从hbase:meta获取想要操作的Region的位置信息,缓存hbase:meta在Client中,用于后续的操作
(3)Client向目标Region所在的RegionServer发送请求,执行操作
(4)当一个region因为Master执行负载均衡或者RegionServer挂掉而执行的重定位之后,Client需要重新读取hbase:meta进行缓存
6.2 写流程
写操作步骤如下:
- Client发起了一个HTable.put(Put)请求给HRegionServer
- HRegionServer会将请求匹配到某个具体的HRegion上面
- 决定是否写WAL log。WAL log文件是一个标准的Hadoop sequenceFile,文件中存储了HLogKey,这些Keys包含了和实际数据对应的序列号,主要用于崩溃恢复。
- Put数据保存到MemStore中,同时检查MemStore状态,如果满了,则触发Flush to Disk请求。
- HRegionServer处理Flush to Disk的请求,将数据写成HFile文件并存到HDFS上,并且存储最后写入的数据序列号,这样就可以知道哪些数据已经存入了永久存储的HDFS中。
PS :在开启了WAL时,当数据写到WAL后,就可以返回成功了。
由于不同的列族会共享region,所以有可能出现,一个列族已经有1000万行,而另外一个才100行。当一个要求region分割的时候,会导致100行的列会同样分布到多个region中。所以,一般建议不要设置多个列族。
6.3 再次读流程
Client访问用户数据之前需要首先访问zookeeper,访问.META.表,能找到用户数据的位置去访问,中间需要多次网络操作,不过client端会做cache缓存。再次读操作步骤如下:
- Client会通过内部缓存的相关的-ROOT-中的信息和.META.中的信息直接连接与请求数据匹配的HRegion server;
- 然后直接定位到该服务器上与客户请求对应的region,客户请求首先会查询该region在内存中的缓存——memstore(memstore是是一个按key排序的树形结构的缓冲区);
- 如果在memstore中查到结果则直接将结果返回给client;
- 在memstore中没有查到匹配的数据,接下来会读已持久化的storefile文件中的数据。storefile也是按key排序的树形结构的文件——并且是特别为范围查询或block查询优化过的,;另外hbase读取磁盘文件是按其基本I/O单元(即 hbase block)读数据的。具体就是过程就是:
如果在BlockCache中能查到要造的数据则这届返回结果,否则就读去相应的storefile文件中读取一block的数据,如果还没有读到要查的数据,就将该数据block放到HRegion Server的blockcache中,然后接着读下一block块儿的数据,一直到这样循环的block数据直到找到要请求的数据并返回结果;如果将该region中的数据都没有查到要找的数据,最后接直接返回null,表示没有找的匹配的数据。当然blockcache会在其大小大于一的阀值(heapsize * hfile.block.cache.size * 0.85)后启动基于LRU算法的淘汰机制,将最老最不常用的block删除。
6.4 WAL Splitting 流程
(1)对/hbase/WALs/,, 目录进行重命名
(2)改成:/hbase/WALs/,,-splitting
(3)当RS被HMaster被认为失效的时候,它可能仍然在运行,所以为了保证已经存在的有效的数据
(4)读取WAL中的Edits信息根据region将属于不同region的edits写到对应的/hbase/<table_name>/<region_id>/recovered.edits/.temp(临时文件),当splitting完成后,临时文件名称会修改为写入该文件的第一个log信息的序列号
(5)在Splitting完成后,涉及到的Region会被分配新的RegionServer,那么在新的RegionServer打开Region时会检查recovered.edits目录下是否有edits文件,如果有则读取edits写入到MemStore中,写入完成后刷新到HFile中并删除edits文件。
7.5 WAL Repaly 流程
(1)WAL Replay流程依赖于WAL Splitting流程
(2)WAL Replay流程实际上就是WAL Splitting流程中第三步
(3)WAL Replay是在打开Region时完成的
7.高级特性
7.1 coprocessor[实验]
HBase开始时作为存储工具,不提供基本的数据处理能力。例如,查询某年的交易总额。使用HBase只能先将该年所有的交易数据查询处理返回到客户端,客户端在进行求和处理。需要注意的时HBase时用于大数据存储,这意味着这些查询出的数据量非常大,而直接将大数据返回给client,会形成网络瓶颈,同时给client带来计算压力。
Coprocessor的作用就是给RegionServer带来运行代码的能力,你可以编写计算逻辑代码,放到client运行。在上述例子中,就是可以对每个RegionServer进行求和,然后将结果返回到client。
7.1.1 Coprocessor种类
Coprocessor分为两类:observer、endpoint。Observer相当于传统RDBMS中的触发器,Endpoint相当于存储过程。其中Observer分为RegionObserver, RegionServerObserver, MasterObserver, WALObserver。
Endpoint基于Google protocol buffers(protobuf)实现。
当有多个coprocessor时,coprocessor执行顺序如下:
7.1.2 Coprocessor使用流程
一般步骤:
a)编程程序,实现相应的类,实现相应的接口方法,如RegionObserver类等;
b)将程序打成Jar包;
c)加载Jar包方法HDFS文件系统中;
d)HBase加载jar包。
简单测试表结构:
简单测试代码:省略部分空白接口方法。实现功能时当查询admin信息时,进行过滤。
public class RegionObserverExample implements RegionObserver {
private static final byte[] ADMIN = Bytes.toBytes("admin");
private static final byte[] COLUMN_FAMILY = Bytes.toBytes("details");
private static final byte[] COLUMN = Bytes.toBytes("Admin_det");
private static final byte[] VALUE = Bytes.toBytes("You can't see Admin details");
@Override
public void preGetOp(final Observercontext<RegionCoprocessorenvironment> e, final
Get get, final List<Cell> results) throws IOException
{
if (Bytes.equals(get.getRow(),ADMIN))
{
Cell c = CellUtil.createCell(get.getRow(),COLUMN_FAMILY, COLUMN,
system.currenttimemillis(), (byte)4, VALUE);
results.add(c);
e.bypass();
}
}
@Override
public Regionscanner preScannerOpen(final ObserverContext<RegionCoprocessorEnvironment> e, final Scan scan,final RegionScanner s) throws IOException {
Filter filter = new RowFilter(CompareOp.NOT_EQUAL, new binaryComparator(ADMIN));
scan.setFilter(filter);
return s;
}
@Override
public boolean postScannerNext(final ObserverContext<RegionCoprocessorEnvironment> e,final InternalScanner s,final List<Result> results, final int limit, final boolean hasMore) throws IOException
{
Result result = null;
Iterator<Result> iterator = results.iterator();
while (iterator.hasNext())
{
result = iterator.next();
if (Bytes.equals(result.getRow(), ADMIN))
{
iterator.remove();
break;
}
}
return hasMore;
}
}
7.1.3 HBase动态加载jar包步骤
以下过程结合上面测试例子。
a)disable ‘users’ ;
b)使用如下命令加载coprocess jar代码
hbase(main):013:0> alter 'hb:users',METHOD=>'table_att','Coprocessor'=> 'hdfs://192.169.82.238:8020/hbase-test/hbase_test.jar|client.RegionObserverExample|10024'
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 3.0730 seconds
在加载时,可能会遇到如下问题:
ERROR: org.apache.hadoop.hbase.DoNotretryIOException: Call From node2/192.169.82.238 to node2:8020 failed on connection exception: java.net.ConnectException: Connection refused; For more details see: http://wiki.apache.org/hadoop/ConnectionRefused Set hbase.table.sanity.checks to false at conf or table descriptor if you want to bypass sanity checks
at org.apache.hadoop.hbase.master.HMaster.warnOrThrowExceptionForFailure(HMaster.java:1814)
at org.apache.hadoop.hbase.master.HMaster.sanityCheckTableDescriptor(HMaster.java:1682)
at org.apache.hadoop.hbase.master.HMaster.modifyTable(HMaster.java:2160)
at org.apache.hadoop.hbase.master.MasterRpcServices.modifyTable(MasterRpcServices.java:1188)
at org.apache.hadoop.hbase.protobuf.generated.MasterProtos$MasterService$2.callBlockingMethod(MasterProtos.java:57202)
at org.apache.hadoop.hbase.ipc.RpcServer.call(RpcServer.java:2127)
at org.apache.hadoop.hbase.ipc.CallRunner.run(CallRunner.java:107)
at org.apache.hadoop.hbase.ipc.RpcExecutor.consumerLoop(RpcExecutor.java:133)
at org.apache.hadoop.hbase.ipc.RpcExecutor$1.run(RpcExecutor.java:108)
at java.lang.Thread.run(Thread.java:745)
解决办法:修改HBase配置文件,增加如下参数
c)enable ‘users’
d)验证是否加载成功,是否有coprocessor属性:
hbase(main):014:0> enable 'hb:users'
hbase(main):016:0> describe 'hb:users'
Table hb:users is ENABLED
hb:users, {TABLE_ATTRIBUTES => {METADATA => {'Coprocessor$1' => 'hdfs://192.169.82.238:8020/hbase-test/hbase_test.jar|client.RegionObserverExample|10024'}}
COLUMN FAMILIES DESCRIPTION
{NAME => 'personalDet', DATA_BLOCK_ENCODING => 'NONE', bloomfilter => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL =>
'FOREVER', KEEP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
{NAME => 'salaryDet', DATA_BLOCK_ENCODING => 'NONE', BLOOMFILTER => 'ROW', REPLICATION_SCOPE => '0', VERSIONS => '1', COMPRESSION => 'NONE', MIN_VERSIONS => '0', TTL => '
FOREVER', KEEP_DELETED_CELLS => 'FALSE', BLOCKSIZE => '65536', IN_MEMORY => 'false', BLOCKCACHE => 'true'}
2 row(s) in 15.1160 seconds
7.2 WAL[恢复]
WAL(write ahead log),在启用WAL后,先写WAL后写memStore。在RegionServer上所有的Region共享一个HLog。
WAL的实现类是HLog,当一个Region被初始化的时候,一个HLog的实例会作为构造函数的参数传进去。
当Region在处理Put、Delete等更新操作时,可以直接使用该共享的HLog的APPend方法来落地数据。
Put、Delete在客户端上可以通过setWriteToWAL(false)方法来关闭该操作的日志,这么做虽然可以提升入库速度,但最好别这么做,因为有数据丢失的风险存在。
7.3 高可用性
HBase的高可用性体现在如下几个方面:
(1)HMaster:HMaster不存在单点故障,当主HMaster出现故障时,backup Master会自动补替上去。
下面是在Ambari中关于HMaster发生故障的一个实验。集群中包含3个节点,其中node3是HMaster节点,node1为standby HMaster节点。
实验步骤:
step1 : Node3节点初始状态:HMaster运行正常。
Step2: Node1节点初始状态:Standy Hbase Master运行正常。
Step3 :模拟HMaster节点故障,关闭node3上的HMaster服务。
Step4 :观察HBase集群状态,Node1自动顶上区,但是在这里显示的还是Standy HBase Master.
可通过RegionServer中看到HBase Master已经切换成Node1.
(2)HRegionServer方面:当HRegionServer出现故障,Region可以进行迁移,这个迁移过程比较快速。
下面是在Ambari中关于HReginServer发生故障的一个实验。集群中包含2个HRegionServer节点,初始状态,hb:twocol表region都处于node3节点上。
Node2节点上的Region状态:
Node3节点上的Region状态:
实验步骤:
Step1 :通过HBase Shell查询hb:twocol表数据。
Step 2:模拟HRegionServer节点故障,关闭Node3上的HRegionServer。
Step3: 查询hb:twocol上的数据,发现数据没有丢失。
Step4: 查看node2 RegionServer数据,验证Region的迁移。
(3)读写时发生故障:通过WAL保证。
(4)底层文件系统:通过HDFS进行保证(默认配置是每个块进行保持3份,但是就HDFS来说,存在NameNode单点故障)。
7.4 结合MapReduce操作HBase[实验]
7.4.1 使用MapReduce做全表扫描
可以使用hbase-server-VERSION.jar直接做全表扫描。在运行该mapreduce程序之间,需要搭建MapReduce集群环境,使得MapReduce能操作HBase数据。该操作步骤:
a)直接修改HADOOPHOME/conf/hadoopenv.sh配置文件,将HBaselib目录加到HadoopclassPath路径下。b)将Hbase的hbase−site.xml文件移到hadoop_home/conf目录下。
建议在操作完后,先检验环境是否配置成功。如果执行如下,则配置成功。
$hadoop jar /usr/hdp/2.5.0.0-1245/hbase/lib/hbase-server-1.1.2.2.5.0.0-1245.jar
An example program must be given as the first argument.
valid program names are:
copytable: Export a table from local cluster to peer cluster
completebulkload: Complete a bulk data load.
export: Write table data to HDFS.
import: Import data written by Export.
importtsv: Import data in TSV format.
rowcounter: Count rows in HBase table
做全表扫描的命令如下:
$hadoop jar /usr/hdp/2.5.0.0-1245/hbase/lib/hbase-server-1.1.2.2.5.0.0-1245.jar rowcounter tablename .
7.4.2 BulkLoad
Bulkload过程主要包括三部分:
a)从数据源(通常是文本文件或其他的数据库)提取数据并上传到HDFS
这一步不在HBase的考虑范围内,不管数据源是什么,只要在进行下一步之前将数据上传到HDFS即可。
b)利用一个MapReduce作业准备数据
这一步需要一个MapReduce作业,并且大多数情况下还需要我们自己编写Map函数,而Reduce函数不需要我们考虑,由HBase提供。该作业需要使用rowkey(行键)作为输出Key,KeyValue、Put或者Delete作为输出Value。MapReduce作业需要使用HFileOutputFormat2来生成HBase数据文件。为了有效的导入数据,需要配置HFileOutputFormat2使得每一个输出文件都在一个合适的区域中。为了达到这个目的,MapReduce作业会使用Hadoop的TotalorderPartitioner类根据表的key值将输出分割开来。HFileOutputFormat2的方法configureIncrementalLoad()会自动的完成上面的工作。
c)告诉RegionServers数据的位置并导入数据
这一步是最简单的,通常需要使用LoadIncrementalHFiles(更为人所熟知是completebulkload工具),将文件在HDFS上的位置传递给它,它就会利用RegionServer将数据导入到相应的区域。
MapReduce bulkLoad流程如下图所示。
测试文本:
执行效果:
结果检验:
8.使用JAVA API操作HBase
8.1 环境配置
(1)将HBase_Home/lib下的所有jar包导入到项目中(通过编辑build path完成,基本开发不需要全部导入);
(2)将HBase_Home/conf下的hbase-site.xml ,logj.properties文件放到项目中;
(3)因为HBase有的类依赖于Hadoop库,所有需要将Hadoop相关lib也导入到项目中。
项目目录结构如下:
8.2示例程序一:创建表
configuration conf = HBaseConfiguration.create();
HBaseAdmin admin=new HBaseAdmin(conf);
HTableDescriptor htd =new HTableDescriptor(TableName.valueOf("hb:threecol"));
htd.addFamily(new HColumnDescriptor("f1"));
htd.addFamily(new HColumnDescriptor("f2"));
htd.addFamily(new HColumnDescriptor("f3"));
admin.createTable(htd);
8.3示例程序二:操作表
Configuration conf = HBaseConfiguration.create();
HTable table=new HTable(conf,Bytes.toBytes("hb:twocol"));
Put put=new Put(Bytes.toBytes("row6"));
HColumnDescriptor colf[]=table.getTableDescriptor().getColumnFamilies();
put.add(Bytes.toBytes("f1"), Bytes.toBytes("col1"), Bytes.toBytes("text6") );
table.put(put);
8.4示例程序四:自定义配置
conf.set("hbase.zookeeper.quorum", "192.169.82.237,192.169.82.238,192.169.82.239");
conf.setInt("hbase.client.retries.number",5 );
conf.setBooleanIfUnset(" HBASE_MANAGES_ZK", false);
conf.set("hbase.master", "192.169.82.237:16000");
conf.set("zookeeper.znode.parent", "/hbase-unsecure");
9.常见问题与疑惑
(1)如何查出历史数据,非最新的版本?
HBase通过row和column确定一份数据,这份数据的值可能有多个版本,不同版本的值按照时间倒序排序,即最新的数据排在最前面,查询时默认返回最新版本。如上例中row key=1的author:nickname值有两个版本,分别为1317180070811对应的“一叶渡江”和1317180718830对应的“yedu”(对应到实际业务可以理解为在某时刻修改了nickname为yedu,但旧值仍然存在)。Timestamp默认为系统当前时间(精确到毫秒),也可以在写入数据时指定该值。
要查询出旧版本的数据可通过指定timestamp或是指定一个timestamp区间。
(2)HBase value怎么定位?
每个值通过4个键唯一索引,tableName+RowKey+ColumnKey+Timestamp=>value,例如上例中{tableName=’blog’,RowKey=’1’,ColumnName=’author:nickname’,Timestamp=’ 1317180718830’}索引到的唯一值是“yedu”。
(3)HBase中存储数据类型都是什么样子?
TableName 是字符串;
RowKey 和 ColumnName 是二进制值(Java 类型 byte[]);
Timestamp 是一个 64 位整数(Java 类型 long);
value 是一个字节数组(Java类型 byte[])。
相关阅读
全栈工程师开发手册 (作者:栾鹏)架构系列文章 HBase中的表一般有这样的特点:1 大:一个表可以有上亿行,上百万列2 面向列:面向列(族)的存