Hbase 全称是Hadoop DataBase ,是一种开源的,可伸缩的,高可靠,高性能,面向列的分布式存储系统。
类似于Google的BigTable,其分布式计算采用MapReduce,通过MapReduce完成大块数据加载和全表扫描操作。文件存储系统是HDFS,通过Zookeeper来完成状态管理协同服务。不过BigTable只支持一级索引,而Hbase支持一级和二级索引。
需要指出的是:Hbase是面向列的数据库是说的Hbase以列簇的模式进行存储,而不是说Hbase本身是面向列的数据库。Hbase充分利用Lee磁盘上列存储格式的特性,它和传统的Columner databases 还是有区别的。Columner databases擅长做实时数据分析访问,而hbase在基于key的单值访问和范围扫描比较突出。
由于Hbase是基于Hadoop Mapreduc来处理数据的,而且数据也是存储在HDFS中,所以您需要先安装hadoop,本文是单机环境。您需要参考本人博客的CDH4_hadoop2.0安装来完成hadoop安装。
安装完成启动之后你只需要下边命令即可完成安装:
Yum install hbase-master
Yum install hbase-rest
启动hbase-master服务
Service hbase-master start
Service hbase-rest start
启动完成之后你需要下边命令进入hbase的控制面板
hbase shell
查看Hbase服务状态
hbase(main):001:0> status
1 servers, 0 dead, 15.0000 average load
查看Hbase版本
hbase(main):003:0> version
0.94.6-cdh4.3.0, rUnknown, Mon May 27 20:22:05 PDT 2013
操作一个信息表来演示HBase用法。创建一个student表,结构如下:
RowKey |
address |
info |
||||
|
City |
contry |
province |
Age |
Birthday |
sex |
zhangsan |
Bj |
Cn |
Bj |
23 |
90 |
man |
这个表中address和info都是有三个列的列簇。
①创建一个表student
hbase(main):007:0> create ‘student‘ ,‘class‘ , ‘class_id‘ , ‘address‘ , ‘info‘
0 row(s) in 1.7210 seconds
=> Hbase::Table - student
②列出所有表
hbase(main):001:0> list
TABLE
student
③查看表结构
hbase(main):019:0> describe ‘student‘
DESCRIPTION ENABLED
{NAME => ‘student‘, FAMILIES => [{NAME => ‘address‘, DATA_BLOCK_ENCODING => ‘ true
NONE‘, BLOOMFILTER => ‘NONE‘, REPLICATION_SCOPE => ‘0‘, VERSIONS => ‘3‘, COMP
RESSION => ‘NONE‘, MIN_VERSIONS => ‘0‘, TTL => ‘2147483647‘, KEEP_DELETED_CEL
LS => ‘false‘, BLOCKSIZE => ‘65536‘, IN_MEMORY => ‘false‘, ENCODE_ON_DISK =>
‘true‘, BLOCKCACHE => ‘true‘}, {NAME => ‘class‘, DATA_BLOCK_ENCODING => ‘NONE
‘, BLOOMFILTER => ‘NONE‘, REPLICATION_SCOPE => ‘0‘, VERSIONS => ‘3‘, COMPRESS
ION => ‘NONE‘, MIN_VERSIONS => ‘0‘, TTL => ‘2147483647‘, KEEP_DELETED_CELLS =
> ‘false‘, BLOCKSIZE => ‘65536‘, IN_MEMORY => ‘false‘, ENCODE_ON_DISK => ‘tru
e‘, BLOCKCACHE => ‘true‘}, {NAME => ‘class_id‘, DATA_BLOCK_ENCODING => ‘NONE‘
, BLOOMFILTER => ‘NONE‘, REPLICATION_SCOPE => ‘0‘, VERSIONS => ‘3‘, COMPRESSI
ON => ‘NONE‘, MIN_VERSIONS => ‘0‘, TTL => ‘2147483647‘, KEEP_DELETED_CELLS =>
‘false‘, BLOCKSIZE => ‘65536‘, IN_MEMORY => ‘false‘, ENCODE_ON_DISK => ‘true
‘, BLOCKCACHE => ‘true‘}, {NAME => ‘info‘, DATA_BLOCK_ENCODING => ‘NONE‘, BLO
OMFILTER => ‘NONE‘, REPLICATION_SCOPE => ‘0‘, VERSIONS => ‘3‘, COMPRESSION =>
‘NONE‘, MIN_VERSIONS => ‘0‘, TTL => ‘2147483647‘, KEEP_DELETED_CELLS => ‘fal
se‘, BLOCKSIZE => ‘65536‘, IN_MEMORY => ‘false‘, ENCODE_ON_DISK => ‘true‘, BL
OCKCACHE => ‘true‘}]}
1 row(s) in 0.0980 seconds
④删除一个列簇。删除一个列簇 需要2步,首先disable表,最后要enable表。
之前简历了3个列簇,但是class和class_id我们用不到,所以准备删除他。
命令如下:
hbase(main):041:0> disable ‘student‘
0 row(s) in 2.4160 seconds
删除:
hbase(main):042:0> alter ‘student‘ , {NAME=>‘class‘,METHOD=>‘delete‘}
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 2.4710 seconds
启用student
hbase(main):006:0> enable ‘student‘
0 row(s) in 2.2060 seconds
⑤删除表
hbase(main):012:0> disable ‘abc‘
0 ro(s) in 2.1720 seconds
hbase(main):014:0> drop ‘abc‘
0 row(s) in 1.1720 seconds
⑥验证表是否存在
hbase(main):001:0> exists ‘abc‘
Table abc does not exist
0 row(s) in 1.0410 seconds
⑦判断表是否enable
hbase(main):005:0> is_enabled ‘student‘
true
0 row(s) in 0.0220 seconds
⑧判断表是否disable
hbase(main):009:0> is_disabled ‘student‘
false
0 row(s) in 0.0090 seconds
⑨增加一个列簇
hbase(main):016:0> alter ‘student‘ , {NAME=>‘test‘ ,VERSIONS=>5}
Updating all regions with the new schema...
1/1 regions updated.
Done.
0 row(s) in 1.1850 seconds
①向表中插入几条数据
hbase(main):026:0> put ‘student‘ , ‘zhangsan‘,‘info:age‘,‘23‘
0 row(s) in 0.0870 seconds
hbase(main):027:0> put ‘student‘ , ‘zhangsan‘,‘info:birthday‘,‘90‘
0 row(s) in 0.0890 seconds
hbase(main):028:0> put ‘student‘ , ‘zhangsan‘,‘info:sex‘,‘man‘
0 row(s) in 0.0830 seconds
hbase(main):029:0> put ‘student‘ , ‘zhangsan‘,‘address:city‘,‘bj‘
0 row(s) in 0.0510 seconds
hbase(main):030:0> put ‘student‘ , ‘zhangsan‘,‘address:contry‘,‘cn‘
0 row(s) in 0.0880 seconds
hbase(main):031:0> put ‘student‘ , ‘zhangsan‘,‘address:provinc‘,‘bj‘
0 row(s) in 0.0100 seconds
②获取一条数据
获取一个ID的所有数据:
hbase(main):049:0> get ‘student‘,‘zhangsan‘
COLUMN CELL
address:city timestamp=1401937717401, value=bj
address:contry timestamp=1401937730454, value=cn
address:provinc timestamp=1401937746616, value=bj
info:age timestamp=1401937660360, value=23
info:birthday timestamp=1401937675146, value=90
info:sex timestamp=1401937698368, value=man
6 row(s) in 0.1020 seconds
③获取一个ID,一个列簇的所有数据
hbase(main):060:0> get ‘student‘ , ‘zhangsan‘ , ‘info‘
COLUMN CELL
info:age timestamp=1401937660360, value=23
info:birthday timestamp=1401937675146, value=90
info:sex timestamp=1401937698368, value=man
3 row(s) in 0.0680 seconds
④获取一个ID,一个列簇中的一个列的所有数据
hbase(main):067:0> get ‘student‘ , ‘zhangsan‘ , ‘info:age‘
COLUMN CELL
info:age timestamp=1401937660360, value=23
1 row(s) in 0.0500 seconds
⑤更新一条记录
将张三的性别改成women
hbase(main):072:0> put ‘student‘, ‘zhangsan‘ , ‘info:sex‘ , ‘women‘
0 row(s) in 0.0810 seconds
hbase(main):073:0> get ‘student‘,‘zhangsan‘,‘info:sex‘
COLUMN CELL
info:sex timestamp=1401938013194, value=women
1 row(s) in 0.0120 seconds
⑥全表扫描
hbase(main):085:0> scan ‘student‘
ROW COLUMN+CELL
zhangsan column=address:city, timestamp=1401937717401, value=bj
zhangsan column=address:contry, timestamp=1401937730454, value=cn
zhangsan column=address:provinc, timestamp=1401937746616, value=bj
zhangsan column=info:age, timestamp=1401937660360, value=23
zhangsan column=info:birthday, timestamp=1401937675146, value=90
zhangsan column=info:sex, timestamp=1401938013194, value=women
1 row(s) in 0.0820 seconds
⑦删除ID为张三的,’info:age’字段的值
hbase(main):095:0> delete ‘student‘ , ‘zhangsan‘ , ‘info:age‘
0 row(s) in 0.0180 seconds
hbase(main):096:0> get ‘student‘ , ‘zhangsan‘
COLUMN CELL
address:city timestamp=1401937717401, value=bj
address:contry timestamp=1401937730454, value=cn
address:provinc timestamp=1401937746616, value=bj
info:birthday timestamp=1401937675146, value=90
info:sex timestamp=1401938013194, value=women
5 row(s) in 0.0180 seconds
⑧查询表中有多少行
hbase(main):114:0> count ‘student‘
1 row(s) in 0.0260 seconds
⑨清空整张表
hbase(main):129:0> truncate ‘student‘
Truncating ‘student‘ table (it may take a while):
- Disabling table...
- Dropping table...
- Creating table...
0 row(s) in 4.4990 seconds
您也可以将你的hbase命令写到一个文件里边,然后执行hbase shell test.sh
即可。
①架构图
图中可以看出,HBase的核心有HMaster ,HregionServer,Hlog,HRegion等。而Hbase外部依赖zookeeper和HMaster。
1)HMaster(类似于Hadoop中的Namenode,Mapreduce中的jobtracker)是用来管理HRegionServer的。它负责监控集群中HRegionServer的状态信息变化。它主要功能点如下:
1.管理HRegionServer的负载均衡,调整Region的分布。这个通过HMaster的后台进程LoadBalancer来完成。LoadBalancer会定期将Region进行移动,以使各个HRegionServer达到Load均衡。
2.在Region Split后,负责新Region的分配。
3.HRegionServer的FailOver处理,当一个HRegionServer宕机之后,Hmaster负责将它的HRegion进行转移。
4.CataLogJanitor。它会定期清理和检查.Meta.表
一个HBase集群中可以有多个HMaster。不过Zookeeper的选举制保证只有一个Hmaster运行。当一个HMaster出问题后,其他的HMaster将启动代替。
2)从结构图中可以看出,HBase客户端只与zookeeper和HRegion Server打交道。并不和HMaster打交道。如果HMaster出问题,短时间内HBase是可以对外提供服务的,但是,因为HMaster掌握HRegionServer的的一些功能,例如:HRegionServer的FailOver操作,所以长时间未回复正常,将影响Hbase对外提供服务的准确度。
3)Hbase有2个CateLog表:-Root- 和.Meta.。-Root-表中存储了.Meta.表的位置。即.Meta.表的Region Server。.Meta.表存储所有的Region位置和每个Region所包含的Rowkey范围。-Root-表存储位置记录在Zookeeper中,表.Meta.的存储位置记录在-Root-表中。
4)当客户端发起一个请求之后流程是什么样的呢?
首先客户端会连接上Zookeeper集群,获取-Root-表存放的在哪一个HRegionServer上,找到HRegionServer后就能根据-Root-表中存放的.Meta.表的位置。客户端根据.Meta.表存储的HRegion位置到相应的HRegionServer中取对应的HRegion的数据信息。经过一次查询以后,访问CataLog表的过程将被缓存起来,下次客户端就可以直接访问HRegion获取数据信息了。
5)Hbase集成了HDFS,最终数据都会通过HDFS的API将数据持久化到HDFS中。
6)一个HBase集群拥有多个HRegionServer,(参考HDFS中的DataNode ,MapReduce中的TaskTracker),由一个HMaster来管理。每个HRegionServer拥有一个WAL(write Ahead Log,日志文件,用作数据恢复)和多个HRegion(可以简单理解为存储为一个表中的某些行)。一个HRegion拥有多个Store(存储一个ColumnFamily列簇)。一个Store又由一个MemStore(持有对该store的所有更改,在内存中)和0到多个StoreFiles(Hfile,数据存储的地方)组成。示意图如下:
行标记,类似于传统数据库表中的行号。RowKeys具有不变性。除非该行被删除或者被重新插入了新的数据。Hbase中支持基于RowKey的单行查询和范围扫描。在Hbase的Auto-Sharding中,也是基于RowKey进行自动切分的。
HBase中基本单元就是列。而列簇是由一个或多个列组成。使用时,一般讲性质差不多的放在一个列簇。因为Hbase是面向列存储的,也就是说一个列簇的所有列是存储在一起的。即上图中一个Store存储一个列簇。
注意一个表中被限定不能超过10个列簇。
HBase支持时间戳的概念。即允许一个Cell存储多个版本值。版本之间通过时间戳来划分。就是说某一列的某一行存在多个值。一般默认是3,最近的版本存在最上面。HBase中有一个TTL(Time to Live)的配置,这是基于列簇维度的,一旦过期,列簇就会自动删除所有行。
HRegionServer是负责服务和管理Region的。类似于我们所说的主从服务器,HMaster就是主服务器,HRegionServer就是从服务器。用户执行CRUD的时候,需要HRegionServer来定位到HRegin来操作。
WAL的全名是Write Ahead Log,类似于Mysql的Binary Log,WAL记录了HRegionServer上的所有的数据变更。一旦这个HRegionServer宕机,导致数据丢失后,WAL就起作用了。平时WAL是不起作用的,只是为了不可预知的错误预备的。
WAL的实现类是HLog。因为在一个HRegionServer中只有一个WAL所以对于一个HRegionServer的所有的HRegion来说WAL是全局的,共享的。当HRegion的实例创建的时候,在HRegionServer实例中的HLog就会作为HRegion的构造函数的参数传递给Hregion。当HRegion接收到一个变更的时候,HRegion就可以直接通过HLog将变更日志追加到共享的WAL中。当然基于性能考虑,HBase还提供了一个setWriteToWAL(false)方法。一旦用户调用该方法,变更日志将不在记录到WAl中。
HLog还有一个重要的特性就是:跟踪变更。在HLog中有一个原子类型的变量,HLog会读取StoreFiles中最大的sequence number(HLog中每一条变更日志都有一个number号,因为对于一个HRegionServer中所有的HRegion都是共享HLog的,所以变更日志会顺序写入WAL,StoreFile中也持有该number),并存放到变量中。这样HLog就知道已经存储到哪个位置了。
WAL还有2个比较重要的类,一个是LogSyncer,另外一个是LogRoller。
在创建表时,有一个参数设置:Deferred Log Flush,默认是false,表示log一旦更新就立即同步到filesystem。如果设置为true,则HRegionServer会缓存那些变更,并由后台任务LogSyncer定时将变更信息同步到filesystem。
2、WAL是有容量限制的,LogRoller是一个后台线程,会定时滚动logfile,用户可以设定这个间隔时间(hbase.regionserver.logroll.period,默认是一小时)。当检查到某个logfile文件中的所有sequence number均小于那个最大的sequence number时,就会将此logfile移到.oldLog目录。
如下是WAL的文件结构,目前WAL采用的是Hadoop的SequenceFile,其存储记录格式是key/value键值对的形式。其中Key保存了HLogkey的实例,HLogKey包含数据所属的表名及RegionName,timeStamp,sequenceNumber等信息。Value保存了WALEdit实例,WALEdit包含客户端每一次发来的变更信息。
在HBase中实现可扩展性和负载均衡的基本单元式Region。Region存储着连续的RowKey数据。刚开始一个表只有一个Region,随着数据的增多,达到某个阀值时就会根据RowKey自动一分为二,每个Region存储着【StartKey,endKey】。随着表的继续增大,那么当前的Region还会继续分裂,每个Region只会由一个HRegionServer服务。这就是所谓的Hbase的AutoSharding特性。当然,Region除了会spilt外,也可能进行合并以减少Region数目(这就是Hbase的compaction特性)
Store是核心存储单元。在一个HRegion中可能存在多个ColumnFamily,那么Store中被指定只能存储一个ColumnFamily。不同的ColumnFamily存储在不同的Store中(所以在创建ColumnFamily时,尽量将经常需要一起访问的列放到一个ColumnFamily中,这样可以减少访问Store的数目)。一个Store由一个MemStore和0至多个StoreFile组成
Hbase在将数据写入StoreFile之前,会先将数据写入MemStore中。MemStore是一个有序的内存缓冲器。当MemStore中数据达到阀值(Flush Size)的时候,HRegionServer就将执行Flush操作,将MemStore中的数据flush到StoreFile中。
当MemStore正在向StoreFile中flush数据的时候,MemStore还是可以向外提供读写数据服务的。这个事通过MesStore的滚动机制实现的,通过滚动MemStore,新的空的块就可以接受变更,而老的满的块就会执行flush操作。
StoreFile是HFile的实现,对HFile做了一层包装。HFile是数据真正存储的地方。HFile是基于BigTable的SSTable File和Hadoop的TFile。HFile是以keyvalue的格式存储数据的。(Hbase之前使用过Hadoop得MapFile,因为其性能上相当糟糕而放弃。)下图是HFile中版本1的格式,版本2稍有改变(详见Hbase wiki):
从上图中看出,HFile是由多个数据块组成。大部分数据块是不定长的,唯一固定长度的只有两个数据块:File Info和Trailer。DataIndex和MetaIndex分别记录了Data块和Meta块的起始位置。每个data块由一些kevalue键值对和Magic header组成。Data块的大小可以再创建表时通过HColumnDescriptor设定。Magic记录了一串随机的数字,防治数据丢失和损坏。
如果用户想绕过Hbase直接访问HFile时,比如检查HFile的健康状态,dump HFile的内容,可以通过HFile.main()方法完成。
如下图是KeyValue的格式:
KeyValue是一个数组,对byte数组做了一层包装。Key Length和Value Length都是固定长度的数值。Key包含的内容有行RowKey的长度及值,列族的长度及值,列,时间戳,key类型(Put, Delete, DeleteColumn, DeleteFamily)。
从上图可以看出,每一个keyValue只包含一列,即使对于同一行的不同列数据,会创建多个KeyValue实例。此外KeyValue不能被Split,即使此KeyValue值超过Block的大小,比如:
Block大小为16Kb,而KeyValue值有8Mb,那么KeyValue会通过相连的多个Block进行存储。
以上对Hbase的基本元素做了一个大体的介绍。下图是Hbase的存储结构图。记录了客户端发起变更或者新增操作时,Hbase内部的存储流程。
下面分析下整个存储流程
1)当客户端提交变更操作的时候,首先客户端连接Zookeeper找到-Root-表,通过-Root-提供的.Meta.表的位置找到.Meta.表,根据.Meta.中信息找到对应的Region所在的HRegionServer。数据变更信息会首先通过HRegionServer写入一个Commit log,也就是WAL。写入WAL成功之后,数据变更信息会储存到MEMStore中,当MemStore达到阀值(默认64MB)的时候,MemStore会执行flush操作,将数据持久化到HFile中。Flush过程中通过MemStore的滚动机制继续对用户提供读写服务。随着Flush操作不断进行,HFile会越来越多。当HFile超过设定数量的时候,Hbase的HouseKeeping机制通过Compaction特性将小的HFile合并成一个更大的HFile文件。在Compaction的过程中,会进行版本的合并以及数据的删除。由于storeFiles是不变的,用户执行删除操作时,并不能简单地通过删除其键值对来删除数据内容。Hbase提供了一个delete marker机制(也称为tombstone marker),会告诉HRegionServer那个指定的key已经被删除了。这样其它用户检索这个key的内容时,因为已经被标记为删除,所以也不会检索出来。在进行Compaction操作中就会丢弃这些已经打标的记录。经过多次Compaction后,HFile文件会越来越大,当达到设定的值时,会触发Split操作。将当前的Region根据RowKey对等切分成两个子Region,当期的那个Region被废弃,两个子Region会被分配到其他HRegionServer上。所以刚开始时一个表只有一个Region,随着不断的split,会产生越来越多的Region,通过HMaster的LoadBalancer调整,Region会均匀遍布到所有的HRegionServer中。
2)当HLog满时,HRegionServer就会启动LogRoller,通过执行rollWriter方法将那些所有sequence number均小于最大的那个sequence number的logfile移动到.oldLog目录中等待被删除。如果用户设置了Deferred Log Flush为true,HRegionServer会缓存有关此表的所有变更,并通过LogSyncer调用sync()方法定时将变更信息同步到filesystem。默认为false的话,一旦有变更就会立刻同步到filesystem
3)在一个HRegionServer中只有一个WAL,所有Region共享此WAL。HLog会根据Region提交变更信息的先后顺序依次顺序写入WAL中。如果用户设置了setWriteToWAL(false)方法,则有关此表的所有Region变更日志都不会写入WAL中。这也是上图中Region将变更日志写入WAL的那个垂直向下的箭头为什么是虚线的原因。
原文:http://blog.csdn.net/qzlzwhx/article/details/28638897