HBase 是一种分布式、可扩展、支持海量数据存储的 NoSQL 数据库。
HBase采用的是Key/Value的存储方式,这意味着,即使随着数据量增大,也几乎不会导致查询的性能下降
HBase又是一个列式数据库(对比于传统的行式数据库而言),当你的表字段很多的时候,你甚至可以把其中几个字段放在集群的一部分机器上,而另外几个字段放到另外一部分机器上,充分分散了负载压力。
然而,如此复杂的存储结构和分布式的存储方式带来的代价就是:哪怕只是存储少量数据,它也不会很快。即:HBase并不快,只是当数据量很大的时候它慢的不明显
当你的情况大体上符合以下任意一种的时候:
请不要使用HBase,使用MySQL或者Oracle之类的产品可以让你的脑细胞免受折磨。
当你的情况是:
请使用HBase,它不会让你失望的。
1)海量存储
Hbase适合存储PB级别的海量数据,在PB级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正式因为Hbase良好的扩展性,才为海量数据的存储提供了便利。
2)列式存储
这里的列式存储其实说的是列族存储,Hbase是根据列族来存储数据的。列族下面可以有非常多的列,列族在创建表的时候就必须指定。
在hbase中列是数据,建表时可以不用指定列,只用指定它的列族
3)极易扩展
Hbase的扩展性主要体现在两个方面,一个是基于上层处理能力(RegionServer)的扩展,一个是基于存储的扩展(HDFS)
通过横向添加RegionSever的机器,进行水平扩展,提升Hbase上层的处理能力,提升Hbsae服务更多Region的能力,即提升业务能力。
备注:RegionServer的作用是管理region、承接业务的访问。
4)高并发
由于目前大部分使用Hbase的架构,都是采用的廉价PC,因此单个IO的延迟其实并不小,一般在几十到上百ms之间。这里说的高并发,主要是在并发的情况下,Hbase的单个IO延迟下降并不多。能获得高并发、低延迟的服务。
5)稀疏
即hbase中存储的数据可以为空(注:mysql中只允许为null,null不等于空)
逻辑上,HBase 的数据模型同关系型数据库很类似,数据存储在一张表中,有行有列。但从 HBase 的底层物理存储结构(K-V)来看,HBase 更像是一个 multi-dimensional map。
这一张表划分成了两个列族和三个Region
**1)Name Space **
命名空间,类似于关系型数据库的 DatabBase 概念,每个命名空间下有多个表。HBase有两个自带的命名空间,分别是 hbase 和 default,hbase 中存放的是 HBase 内置的表,default 表是用户默认使用的命名空间。
2)列族
在HBase中,若干列可以组成列族(column family)。建表的时候是不需要制定列的,因为列是可变的,它非常灵活,唯一需要确定的就是列族。这就是为什么说一个表有几个列族是一开始就定好的。这一点做法跟以往的数据库有很大的区别。同一个表里的不同列族可以有完全不同的属性配置,但是同一个列族内的所有列都会有相同的属性,因为他们都在一个列族里面,而属性都是定义在列族上的。一个没有列族的表是没有意义的,因为列必须依赖列族而存在。在HBase中一个列的名称前面总是带着它所属的列族。列名称的规范是列族:列名,比如brother:age、brother:name、parent:age、parent:name。
列族存在的意义是:HBase会把相同列族的列尽量放在同一台机器上,所以说,如果想让某几个列被放到一起,你就给他们定义相同的列族。
提示:一个表要设置多少个列族比较合适?
官方的建议是:越少越好,因为HBase并不希望大家指定太多的列族。为什么?因为没有必要,虽然HBase是分布式数据库,但是数据在同一台物理机上依然会加速数据的查询过程。所以请根据实际需要来指定列族,列族太多会极大程度地降低数据库性能;而且根据目前的HBase实现,列族定得太多,容易出BUG。
3)Region
Region就是一段数据的集合。HBase中的表一般拥有一个到多个Region。Region有以下特性:
4)Rowkey
行键(rowkey)完全是由用户指定的一串不重复的字符串,规则随你定!不过,话虽如此,你定的rowkey可是会直接决定这个row的存储位置的。HBase中无法根据某个column来排序,系统永远是根据rowkey来排序的。因此,rowkey就是决定row存储顺序的唯一凭证。而这个排序也很简单:根据字典排序。
比如,以下三个rowkey:
row-1
row-2
row-11
根据字典排序结果:
row-1
row-11
row-2
如果你插入HBase的时候,不小心用了之前已经存在的rowkey,那你就会把之前存在的那个row更新掉。
之前已经存在的值,会被放到这个单元格的历史记录里面,并不会丢掉,只是你需要带上版本参数才可以找到这个值。(这就是列的版本的使用了)
**5)Column **
HBase 中的每个列都由 Column Family(列族)和 Column Qualifier(列限定符)进行限定,例如 info:name,info:age。建表时,只需指明列族,而列限定符无需预先定义。
6)Cell
由{rowkey, column Family:column Qualifier, time Stamp} 唯一确定的单元。cell 中的数据是没有类型的,全部是字节码形式存贮。一个列上可以存储多个版本的单元格。单元格就是数据存储的最小单元。
7)Time Stamp
用于标识数据的不同版本(version),每条数据写入时,如果不指定时间戳,系统会自动为其加上该字段,其值为写入 HBase 的时间。
一般一个HBase集群有一个Master服务器和几个RegionServer服务器。Master服务器负责维护表结构信息,实际的数据都存储在RegionServer服务器上。HBase有一点很特殊:客户端获取数据由客户端直连RegionServer的,所以你会发现Master挂掉之后你依然可以查询数据,但就是不能新建表了。
RegionServer是直接负责存储数据的服务器。RegionServer保存的表数据直接存储在Hadoop的HDFS上
RegionServer非常依赖ZooKeeper服务,可以说没有ZooKeeper就没有HBase。ZooKeeper在HBase中扮演的角色类似一个管家。ZooKeeper管理了HBase所有RegionServer的信息,包括具体的数据段存放在哪个RegionServer上。
客户端每次与HBase连接,其实都是先与ZooKeeper通信,查询出哪个RegionServer需要连接,然后再连接RegionServer。
**架构角色:
1)Region Server **
Region Server 为 Region 的管理者,其实现类为 HRegionServer,直观上说就是服务器上的一个服务。一般来说,一个服务器只会安装一个RegionServer服务,不过你在一个服务器上装多个RegionServer服务也不是不可以。当客户端从ZooKeeper获取RegionServer的地址后,它会直接从RegionServer获取数据。
主要作用如下:
**2)Master **
Master 是所有 Region Server 的管理者,其实现类为 HMaster。
在HBase中Master的角色不像领导,更像是打杂的。客户端从 ZooKeeper获取了RegionServer的地址后,会直接从RegionServer获取数据。其实不光是获取数据,包括插入、删除等所有的数据操作都是直接操作RegionServer,而不需要经过Master。
Master只负责各种协调工作,比如建表、删表、移动Region、合并等操作。它们的共性就是需要跨RegionServer,这些操作由哪个RegionServer来执行都不合适,所以HBase就将这些操作放到了Master上了。
这种结构的好处是大大降低了集群对Master的依赖。而Master节点一般只有一个到两个,一旦宕机,如果集群对Master的依赖度很大,那么就会产生单点故障问题。在HBase中,即使Master宕机了,集群依然可以正常地运行,依然可以存储和删除数据。
主要作用如下:
**3)Zookeeper **
HBase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口以及集群配置的维护等工作。具体工作如下:
当客户端需要查询数据时,先找到zk,从里面的元数据得知数据的具体位置,然后再去找到这个数据,类似于hdfs里面的namenode
通过Zoopkeeper来保证集群中只有1个master在运行,如果master异常,会通过竞争机制产生新的master提供服务
通过Zoopkeeper来监控RegionServer的状态,当RegionSevrer有异常的时候,通过回调的形式通知Master RegionServer上下线的信息
通过Zoopkeeper存储元数据的统一入口地址
**4)HDFS **
HDFS 为 HBase 提供最终的底层数据存储服务,同时为 HBase 提供高可用的支持。具体功能概括如下:
提供元数据和表数据的底层分布式存储服务
数据多副本,保证的高可靠和高可用性
首先保证 Zookeeper 集群的正常部署,并启动之:
[root@h1 app]# zkServer.sh start
[root@h2 app]# zkServer.sh start
[root@h3 app]# zkServer.sh start
Hadoop 集群的正常部署并启动:
[root@h1 hadoop-2.7.7]# sbin/start-all.sh
解压 Hbase 到指定目录:
[root@h1 download]# tar -zxvf hbase-1.3.1-bin.tar.gz -C /root/app
重命名
[root@h1 app]# mv hbase-1.3.1/ hbase
修改 HBase 对应的配置文件。
1)hbase-env.sh 修改内容:
export JAVA_HOME=/root/app/jdk1.8.0_201
export HBASE_MANAGES_ZK=false
并将以下两行注释掉
export HBASE_MASTER_OPTS="$HBASE_MASTER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"
export HBASE_REGIONSERVER_OPTS="$HBASE_REGIONSERVER_OPTS -XX:PermSize=128m -XX:MaxPermSize=128m"
2)hbase-site.xml 修改内容:
hbase.rootdir
hdfs://h1:9000/HBase
hbase.cluster.distributed
true
hbase.master.port
16000
hbase.zookeeper.quorum
h1,h2,h3
hbase.zookeeper.property.dataDir
/root/app/zookeeper/data
3)regionservers:
h1
h2
h3
4)软连接 hadoop 配置文件到 HBase:
[root@h1 conf]# ln -s /root/app/hadoop-2.7.7/etc/hadoop/core-site.xml /root/app/hbase/conf/core-site.xml
[root@h1 conf]# ln -s /root/app/hadoop-2.7.7/etc/hadoop/hdfs-site.xml /root/app/hbase/conf/hdfs-site.xml
**1.启动方式 **
[root@h1 hbase]# bin/hbase-daemon.sh start master
[root@h1 hbase]# bin/hbase-daemon.sh start regionserver
注:master只用在h1启动就好了,regionserver需要在三台都启动
提示:如果集群之间的节点时间不同步,会导致 regionserver 无法启动,抛出
ClockOutOfSyncException 异常。
修复提示:
a、同步时间服务
b、属性:hbase.master.maxclockskew 设置更大的值
hbase.master.maxclockskew
180000
Time difference of regionserver from master
**2.启动方式 2 **
[root@h1 hbase]# bin/start-hbase.sh
对应的停止服务:
[root@h1 hbase]# bin/stop-hbase.sh
启动成功后,可以通过“host:port”的方式来访问 HBase 管理页面,例如:
http://192.168.48.101:16010
1.进入 HBase 客户端命令行
[root@h1 hbase]# bin/hbase shell
2.查看帮助命令
hbase(main):001:0> help
3.查看当前数据库中有哪些表
hbase(main):002:0> list
注:HBase中不能直接用delete,删除数据的话是ctrl+delete(有点反人类哈)
1.创建表
hbase(main):001:0> create 'student','info'
这句话的意思是建立一个叫student的表,这个表里面有一个列族,这个列族叫info。我们之前说过:
所以HBase的表跟列之间的关系中间还有一层:列族
我的表属性要定义在哪里
它们都定义在列族上。HBase的所有数据属性都是定义在列族上的。同一个表的不同列族可以定义完全不同的两套属性,所以从这个意义上来说,列族更像是传统关系数据库中的表,而表本身反倒变成只是存放列族的空壳了。
2.插入数据到表
hbase中没有什么类型之分,只有byte数组
hbase(main):002:0> put 'student','1001','info:sex','male'
hbase(main):003:0> put 'student','1001','info:age','18'
hbase(main):004:0> put 'student','1002','info:name','Janna'
hbase(main):005:0> put 'student','1002','info:sex','female'
hbase(main):006:0> put 'student','1002','info:age','20'
3.用scan来查看表数据
hbase(main):007:0> scan 'student'
hbase(main):009:0> scan 'student',{STARTROW => '1001', STOPROW => '1001'}
hbase(main):010:0> scan 'student',{STARTROW => '1001'}
注:STOPROW或者用ENDROW都可以用于表示结束行,随便用哪个都可以
4.查看表属性
hbase(main):011:0> describe 'student'
NAME这个属性表示的是列族的名称,而不是表的名称,而且后面的所有属性都是针对列族的而不是针对表的。
5.增加一个列族
alter 'student','info2'
可以看到现在describe输出的是两个元素,分别对应info和info2两个列族。
6.用put命令来插入数据
hbase(main):012:0> put 'student','1001','info:name','Nick'
hbase(main):013:0> put 'student','1001','info:age','100'
第一条语句的意思就是:
7.用get来获取单元格数据
hbase(main):012:0> get 'student','1001'
hbase(main):013:0> get 'student','1002','info:name'
我们可以看到这里的时间戳属性。每一个单元格都可以存储多个版本(version)的值。HBase的单元格并没有version这个属性,它用timestamp来存储该条记录的时间戳,这个时间戳就用来当版本号使用。如果你在写put语句的时候不指定时间戳,系统就会自动用当前时间帮你指定它。指定的方式就是就直接在语句的末尾加上时间戳就行,例
put 'student','1001','info:name','Nick',2222222222222
这个时间戳指定的时间明显比上前时间晚,因此它会覆盖掉我们之前插入的内容,但是之前的内容并没有丢,我们可以通过指定版本号来查看之前的内容
get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}
我们发现在get或者scan的输出结果中,HBase并没有专门的一个列族的栏来显示列族这个属性。它总是把列族和列用“列族:列”的组合方式来一起显示,无论是put存储还是scan的查询使用的列定义,都是“列族:列”的格式。比如,info:name表示列族为info,列为name。
8.统计表数据行数
hbase(main):014:0> count 'student'
9.用delete来删除数据
删除某rowkey的全部数据:
hbase(main):016:0> deleteall 'student','1001'
删除某rowkey的某一列数据:
hbase(main):017:0> delete 'student','1002','info:sex'
删除某rowkey的某一列中版本为2的数据:
hbase(main):018:0> delete 'student','1002','info:sex',2
HBase删除记录并不是真的删除了数据,而是放置了一个墓碑标记(tombstone marker),把这个版本连同之前的版本都标记为不可见了。。这是为了性能着想,这样HBase就可以定期去清理这些已经被删除的记录,而不用每次都进行删除操作。
被打上墓碑标记(tombstone marker)的记录还可以被查询到
在记录被真正删除之前还是可以查询到的,只需要在scan命令后跟上RAW=>true参数和适当的VERSIONS参数就可以看到。
被标定为DeleteColumn的列就是被打上墓碑标记的记录。
10.清空表数据
hbase(main):019:0> truncate 'student'
提示:清空表的操作顺序为先disable,然后再truncate。
11.删除表
首先需要先让该表为disable状态:
hbase(main):020:0> disable 'student'
然后才能drop这个表:
hbase(main):021:0> drop 'student'
提示:如果直接drop表,会报错:ERROR: Table student is enabled. Disable it first.
12.变更表信息
将info列族中的数据存放3个版本:
hbase(main):022:0> alter 'student',{NAME=>'info',VERSIONS=>3}
hbase(main):023:0> get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}