一、HBASE概述
官方网址:http://hbase.apache.org/
HBase是一个分布式的、面向列的开源数据库,该技术来源于Fay Chang 所撰写的Google论文《Bigtable》一个结构
化数据的分布式存储系统"。就像Bigtable利用了Google文件系统(File System)所提供的分布式数据存储一样,HBase
在Hadoop之上提供了类似于Bigtable的能力(低延迟的数据查询能力)。HBase是Apache的Hadoop项目的子项
目。HBase不同于一般的关系数据库,Hbase同BigTable一样,都是NoSQL数据库,即非关系型数据库,此外,
HBase和BigTable一样,是基于列的而不是基于行的模式。
HBase特点总结
- 非关系型数据库,可以存储海量数据(基于Hadoop的HDFS)
- 提供了低延迟的数据查询能力。低延迟指的是能够在秒级甚至毫秒级给出响应。
- 面向列存储的数据库。(mysql,oracle都是面向行存储的)
HDFS来源于《Google File System》
Hadoop的MapReduce《Google MapReduce》
HBase来源于Google的《Bigtable》
Zookeeper来源Google的《The Chubby lock service for loosely coupled distributed system》
非关系型数据库和关系型数据库
传统关系型数据库的缺陷
随着互联网Web 2.0的兴起,传统的关系数据库在应付Web 2.0网站,特别是超大规模和高并发的SNS类型动态网
站时已经力不从心,暴露了很多难以克服的问题。
1)高并发读写的瓶颈
Web 2.0网站要根据用户个性化信息来实时生成动态页面和提供动态信息,所以基本上无法使用静态化技术,因
此数据库并发负载非常高,可能峰值会达到每秒上万次读写请求。关系型数据库应付上万次SQL查询还勉强顶得
住,但是应付上万次SQL写数据请求,硬盘I/O却无法承受。其实对于普通的BBS网站,往往也存在相对高并发写
请求的需求,例如,人人网的实时统计在线用户状态,记录热门帖子的点击次数,投票计数等,这是一个相当普
遍的业务需求。
2)可扩展性的限制
在基于Web的架构中,数据库是最难以进行横向扩展的,当应用系统的用户量和访问量与日俱增时,数据库系统
却无法像Web Server和App Server那样简单地通过添加更多的硬件和服务节点来扩展性能和负载能力。对于很多需
要提供24小时不间断服务的网站来说,对数据库系统进行升级和扩展是非常痛苦的事情,往往需要停机维护和数
据迁移,而不能通过横向添加节点的方式实现无缝扩展。
3)事务一致性的负面影响
事务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。保证数据库一致性是指当事务完成
时,必须使所有数据都具有一致的状态。在关系型数据库中,所有的规则必须应用到事务的修改上,以便维护所
有数据的完整性,这随之而来的是性能的大幅度下降。很多Web系统并不需要严格的数据库事务,对读一致性的
要求很低,有些场合对写一致性要求也不高。因此数据库事务管理成了高负载下的一个沉重负担。
4)复杂SQL查询的弱化
任何大数据量的Web系统都非常忌讳几个大表间的关联查询,以及复杂的数据分析类型的SQL查询,特别是SNS类
型的网站,从需求以及产品设计角度就避免了这种情况的产生。更多的情况往往只是单表的主键查询,以及单表
的简单条件分页查询,SQL的功能被极大地弱化了,所以这部分功能不能得到充分发挥。
NoSQL数据库的优势
1)扩展性强
NoSQL数据库种类繁多,但是一个共同的特点就是去掉关系型数据库的关系特性,若数据之间是弱关系,则非常
容易扩展。一般来说,NoSql数据库的数据结构都是Key-Value字典式存储结构。例如,HBase、Cassandra等系
统的水平扩展性能非常优越,非常容易实现支撑数据从TB到PB级别的过渡。
2)并发性能好
NoSQL数据库具有非常良好的读写性能,尤其在大数据量下,同样表现优秀。当然这需要有优秀的数据结构和算
法做支撑。
3)数据模型灵活
NoSQL无须事先为要存储的数据建立字段,随时可以存储自定义的数据格式。而在关系型数据库中,增删字段是
一件非常麻烦的事情。对于数据量非常大的表,增加字段简直就是一场噩梦。NoSQL允许使用者随时随地添加字
段,并且字段类型可以是任意格式。
总结
HBase作为NoSQL数据库的一种,当然也具备上面提到的种种优势。
Hadoop最适合的应用场景是离线批量处理数据,其离线分析的效率非常高,Hadoop针对数据的吞吐量做了大量
优化,能在分钟级别处理TB级的数据,但是Hadoop不能做到低延迟的数据访问,所以一般的应用系统并不适合
批量模式访问,更多的还是用户的随机访问,就类似访问关系型数据库中的某条记录一样。
比如Google这个搜索引擎,存储了海量的网页数据,当我们通过搜索引擎检索一个网页时,之所以Google能够
快速的响应结果,核心的技术就是利用了BigTable,可以实现低延迟的数据访问。
HBase的列式存储的特性支撑它实时随机读取、基于KEY的特殊访问需求。当然,HBase还有不少新特性,其中不乏有趣的特性,在接下来的内容中将会详细介绍。
BigTable介绍
BigTable是Google设计的分布式数据存储系统,用来处理海量的数据的一种非关系型的数据库。
BigTable是非关系的数据库,是一个稀疏的、分布式的、持久化存储的多维度排序Map。Bigtable的设计目的是可靠
的处理PB级别的数据,并且能够部署到上千台机器上。Bigtable已经实现了下面的几个目标:适用性广泛、可扩
展、高性能和高可用性。Bigtable已经在超过60个Google的产品和项目上得到了应用,包括 Google
Analytics、GoogleFinance、Orkut、Personalized Search、Writely和GoogleEarth。这些产品对Bigtable提出了迥异的
需求,有的需要高吞吐量的批处理,有的则需要及时响应,快速返回数据给最终用户。它们使用的Bigtable集群的
配置也有很大的差异,有的集群只有几台服务器,而有的则需要上千台服务器、存储几百TB的数据。
Bigtable的用三维表来存储数据,分别是行键(row key)、列键(column key)和时间戳(timestamp),
本质上说,Bigtable是一个键值(key-value)映射。按作者的说法,Bigtable是一个稀疏的,分布式的,持久化
的,多维的排序映射。可以用(row:string, column:string, time:int64)→string 来表示一条键值对记录。
行存储 VS 列存储
概述
目前大数据存储有两种方案可供选择:行存储(Row-Based)和列存储(Column-Based)。业界对两种存储方案有很多争
持,集中焦点是:谁能够更有效地处理海量数据,且兼顾安全、可靠、完整性。从目前发展情况看,关系数据库已经不适应这
种巨大的存储量和计算要求,基本是淘汰出局。在已知的几种大数据处理软件中,Hadoop的HBase采用列存储,MongoDB是
文档型的行存储,Lexst是二进制型的行存储。
什么是列存储?
列式存储(column-based)是相对于传统关系型数据库的行式存储(Row-basedstorage)来说的。简单来说两者的区别就是如
何组织表:
Ø Row-based storage stores atable in a sequence of rows.
Ø Column-based storage storesa table in a sequence of columns.
从上图可以很清楚地看到,行式存储下一张表的数据都是放在一起的,但列式存储下都被分开保存了。所以它们就有了如下
这些优缺点对比:
在数据写入上的对比
1)行存储的写入是一次完成。如果这种写入建立在操作系统的文件系统上,可以保证写入过程的成功或者失败,数据的完整
性因此可以确定。
2)列存储由于需要把一行记录拆分成单列保存,写入次数明显比行存储多(意味着磁头调度次数多,而磁头调度是需要时间
的,一般在1ms~10ms),再加上磁头需要在盘片上移动和定位花费的时间,实际时间消耗会更大。所以,行存储在写入上占
有很大的优势。
3)还有数据修改,这实际也是一次写入过程。不同的是,数据修改是对磁盘上的记录做删除标记。行存储是在指定位置写入一
次,列存储是将磁盘定位到多个列上分别写入,这个过程仍是行存储的列数倍。所以,数据修改也是以行存储占优。
在数据读取上的对比
1)数据读取时,行存储通常将一行数据完全读出,如果只需要其中几列数据的情况,就会存在冗余列,出于缩短处理时间的
考量,消除冗余列的过程通常是在内存中进行的。
2)列存储每次读取的数据是集合的一段或者全部,不存在冗余性问题。
3) 两种存储的数据分布。由于列存储的每一列数据类型是同质的,不存在二义性问题。比如说某列数据类型为整型(int),那
么它的数据集合一定是整型数据。这种情况使数据解析变得十分容易。相比之下,行存储则要复杂得多,因为在一行记录中
保存了多种类型的数据,数据解析需要在多种数据类型之间频繁转换,这个操作很消耗CPU,增加了解析的时间。所以,列存
储的解析过程更有利于分析大数据。
4)从数据的压缩以及更性能的读取来对比
优缺点
显而易见,两种存储格式都有各自的优缺点:
1)行存储的写入是一次性完成,消耗的时间比列存储少,并且能够保证数据的完整性,缺点是数据读取过程中会产生冗余数
据,如果只有少量数据,此影响可以忽略;数量大可能会影响到数据的处理效率。
2)列存储在写入效率、保证数据完整性上都不如行存储,它的优势是在读取过程,不会产生冗余数据,这对数据完整性要求
不高的大数据处理领域,比如互联网,犹为重要。
两种存储格式各自的特性都决定了它们的使用场景。
列存储的适用场景
1)一般来说,一个OLAP类型的查询可能需要访问几百万甚至几十亿个数据行,且该查询往往只关心少数几个数据列。例
如,查询今年销量最高的前20个商品,这个查询只关心三个数据列:时间(date)、商品(item)以及销售量(sales
amount)。商品的其他数据列,例如商品URL、商品描述、商品所属店铺,等等,对这个查询都是没有意义的。
而列式数据库只需要读取存储着“时间、商品、销量”的数据列,而行式数据库需要读取所有的数据列。因此,列式数据库
大大地提高了OLAP大数据量查询的效率
OLTP OnLine Transaction Processor 在线联机事务处理系统(比如Mysql,Oracle等产品)
OLAP OnLine Analaysier Processor 在线联机分析处理系统(比如Hive Hbase等)
2)很多列式数据库还支持列族(column group,Bigtable系统中称为locality group),即将多个经常一起访问的数据列的
各个值存放在一起。如果读取的数据列属于相同的列族,列式数据库可以从相同的地方一次性读取多个数据列的值,避免了
多个数据列的合并。列族是一种行列混合存储模式,这种模式能够同时满足OLTP和OLAP的查询需求。
3)此外,由于同一个数据列的数据重复度很高,因此,列式数据库压缩时有很大的优势。
例如,Google Bigtable列式数据库对网页库压缩可以达到15倍以上的压缩率。另外,可以针对列式存储做专门的索引优化。
比如,性别列只有两个值,“男”和“女”,可以对这一列建立位图索引:
如下图所示
“男”对应的位图为100101,表示第1、4、6行值为“男”
“女”对应的位图为011010,表示第2、3、5行值为“女”
如果需要查找男性或者女性的个数,只需要统计相应的位图中1出现的次数即可。另外,建立位图索引后0和1的重复度高,可
以采用专门的编码方式对其进行压缩。
当然,如果每次查询涉及的数据量较小或者大部分查询都需要整行的数据,列式数据库并不适用。
最后总结如下
###传统行式数据库的特性如下:
①数据是按行存储的。
②没有索引的查询使用大量I/O。比如一般的数据库表都会建立索引,通过索引加快查询效率。
③建立索引和物化视图需要花费大量的时间和资源。
④面对查询需求,数据库必须被大量膨胀才能满足需求。
列式数据库的特性如下:
①数据按列存储,即每一列单独存放。
②数据即索引。
③只访问查询涉及的列,可以大量降低系统I/O。
④每一列由一个线程来处理,即查询的并发处理性能高。
⑤数据类型一致,数据特征相似,可以高效压缩。比如有增量压缩、前缀压缩算法都是基于列存储的类型定制的,所以可以
大幅度提高压缩比,有利于存储和网络输出数据带宽的消耗。
HBASE单机安装
单机模式安装
特点:不依赖于Hadoop的HDFS,配置完既可使用,好处是便于测试。坏处是不具备分布式
存储数据的能力。
安装配置
- 安装JDK及配置环境变量
- 上传解压Hbase安装包
- 修改Hbase的配置文件,(修改安装目录下的conf/hbase-site.xml)
配置示例:
<property>
<name>hbase.rootdir</name>
<value>file:///home/software/hbase/tmp</value>
</property>
这个是配置hbase存储数据的目录,如果不配置,默认是放在Linux的/tmp目录下。
- 启动hbase,进入bin目录
执行:sh start-hbase.sh
然后可以通过jps查看是否有HMaster进程,如果有,证明hbase启动成功 - 在bin目录执行:
./hbase shell
进入shell客户端操作hbase
HBASE基本概念
HBase以表的形式存储数据。表有行和列族组成。列族划分为若干个列
1)R ow Key
hbase本质上也是一种Key-Value存储系统。Key相当于RowKey,Value相当于列族数据的集
合。
与nosql数据库们一样,row key是用来检索记录的主键。访问hbase table中的行,只有三种方
式:
1 通过单个row key访问
2 通过row key的range
3 全表扫描
Row key行键 (Row key)可以是任意字符串( (最大长度是 64KB,实际应用中长度一般为
10-100bytes),在hbase内部,row key保存为字节数组。
存储时,数据按照Row key的字典序(byte order)排序存储。设计key时,要充分排序存储这个
特性,将经常一起读取的行存储放到一起。(位置相关性)
注意:
字典序对int排序的结果是
1,10,100,11,12,13,14,15,16,17,18,19,2,20,21,…,9,91,92,93,94,95,96,97,98,99。要保
持整形的自然序,行键必须用0作左填充。
2)列族(列簇)
hbase表中的每个列,都归属与某个列族。列族是表的schema的一部分(而列不是),列族必须
在使用表之前定义。列名都以列族作为前缀。例如 courses:history , courses:math 都属于
courses 这个列族。
访问控制、磁盘和内存的使用统计都是在列族层面进行的。实际应用中,列族上的控制权限能
帮助我们管理不同类型的应用:我们允许一些应用可以添加新的基本数据、一些应用可以读取
基本数据并创建继承的列族、一些应用则只允许浏览数据(甚至可能因 为隐私的原因不能浏
览所有数据)。
3)Cell与时间戳
由 {row key, column( = + < label> ), version} 唯一确定的单元。cell中的数据是
没有类型的,全部是字节码形式存贮。
每个 cell都保存着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整
型。时间戳可以由hbase(在数据写入时自动 )赋值,此时时间戳是精确到毫秒的当前系统时
间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版本冲突,就必须自己生成具有
唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前
面。
为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,hbase提供了两种数据版本
回收方式。一是保存数据的最后n个版本,二是保存最近一段时间内的版本(比如最近七
天)。用户可以针对每个列族进行设置。
HBASE基础指令
常用命令
文件->属性->终端->键盘
->delete键序列[VT220Del]
->backspace键序列[ASCII127]
HBASE完全分布式安装
实现步骤
- 准备三台虚拟机,01作为主节点,02、03作为从节点。(把每台虚拟机防火墙都关掉,配
置免密码登录,配置每台的主机名和hosts文件。) - 01节点上安装和配置:Hadoop+Hbase+JDK+Zookeeper
- 02、03节点上安装和配置:Hbase+JDK+Zookeeper
- 修改conf/hbase-env.sh
配置示例:
#修改JAVA_HOME
export JAVA_HOME=xxxx
#修改Zookeeper和Hbase的协调模式,hbase默认使用自带的zookeeper,如果需要使用外
部zookeeper,需要先关闭。
export HBASE_MANAGES_ZK=false - 修改hbase-site.xml,配置开启完全分布式模式
配置示例:
<property>
</property>
<name>hbase.cluster.distributed</name>
<value>true</value>
<property>
</property>
#配置Zookeeper的连接地址与端口号
<name>hbase.zookeeper.quorum</name>
<value>hadoop01:2181,hadoop02:2181,hadoop03:2181</value>
<property>
</property>
- 配置region服务器,修改conf/regionservers文件,每个主机名独占一行,hbase启动或关闭
时会按照该配置顺序启动或关闭主机中的hbase
配置示例:
hadoop01
hadoop02
hadoop03
- 将01节点配置好的hbase通过远程复制拷贝到02,03节点上
- 启动01,02,03的Zookeeper服务
- 启动01节点的Hadoop
- 启动01节点的Hbase,进入到hbase安装目录下的bin目录
执行:sh start-hbase.sh - 查看各节点的java进程是否正确
- 通过浏览器访问http://xxxxx:60010来访问web界面,通过web见面管理hbase
- 关闭Hmaster,进入到hbase安装目录下的bin目录
执行:stop-hbase.sh - 关闭regionserver,进入到hbase安装目录下的bin目录
执行:sh hbase-daemon.sh stop regionserver
注:HBASE配置文件说明
hbase-env.sh配置HBase启动时需要的相关环境变量
hbase-site.xml配置HBase基本配置信息
HBASE启动时默认使用hbase-default.xml中的配置,如果需要可以修改hbase-site.xml文
件,此文件中的配置将会覆盖hbase-default.xml中的配置
修改配置后要重启hbase才会起作用
HBASE API
实现步骤:
1.导入开发包将hbase安装包中lib下包导入java项目
创建表:
@Test
public void testCreateTable() throws Exception{
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
HBaseAdmin admin = new HBaseAdmin(conf);
//指定表名
HTableDescriptor tab1=new HTableDescriptor(TableName.valueOf("tab1"));
//指定列族名
HColumnDescriptor colfam1=new HColumnDescriptor("colfam1".getBytes());
HColumnDescriptor colfam2=new HColumnDescriptor("colfam2".getBytes());
//指定历史版本存留上限
colfam1.setMaxVersions(3);
tab1.addFamily(colfam1);
tab1.addFamily(colfam2);
//创建表
admin.createTable(tab1);
admin.close();
}
插入数据:
@Test
public void testInsert() throws Exception{
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
//尽量复用Htable对象
HTable table=new HTable(conf,"tab1");
Put put=new Put("row-1".getBytes());
//列族,列,值
put.add("colfam1".getBytes(),"col1".getBytes(),"aaa".getBytes());
put.add("colfam1".getBytes(),"col2".getBytes(),"bbb".getBytes());
table.put(put);
table.close();
}
试验: 100万条数据的写入
@Test
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
HTable table=new HTable(conf,"tab1");
List<Put> puts=new ArrayList<>();
long begin=System.currentTimeMillis();
Put put=new Put(("row"+i).getBytes());
put.add("colfam1".getBytes(),"col".getBytes(),(""+i).getBytes());
puts.add(put);
//批处理,批大小为:10000
table.put(puts);
puts=new ArrayList<>();
if(i%10000==0){
}
for(int i=1;i<1000000;i++){
}
long end=System.currentTimeMillis();
System.out.println(end-begin);
}
获取数据:
@Test
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
HTable table=new HTable(conf,"tab1");
Get get=new Get("row1".getBytes());
Result result=table.get(get);
byte[] col1_result=result.getValue("colfam1".getBytes(),"col".getBytes());
System.out.println(new String(col1_result));
table.close();
public void testGet() throws Exception{
}
获取数据集:
@Test
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
public void testScan() throws Exception{
HTable table = new HTable(conf,"tab1");
//获取row100及以后的行键的值
Scan scan = new Scan("row100".getBytes());
ResultScanner scanner = table.getScanner(scan);
Iterator it = scanner.iterator();
Result result = (Result) it.next();
byte [] bs = result.getValue(Bytes.toBytes("colfam1"),Bytes.toBytes("col"));
String str = Bytes.toString(bs);
System.out.println(str);
while(it.hasNext()){
Result result = (Result) it.next();
byte [] bs = result.getValue(Bytes.toBytes("colfam1"),Bytes.toBytes("col"));
String str = Bytes.toString(bs);
System.out.println(str);
}
table.close();
}
删除数据:
@Test
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
public void testDelete() throws Exception{
HTable table = new HTable(conf,"tab1");
Delete delete=new Delete("row1".getBytes());
table.delete(delete);
table.close();
}
删除表
@Test
Configuration conf=HBaseConfiguration.create();
"192.168.234.11:2181,192.168.234.210:2181,192.168.234.211:2181");
conf.set("hbase.zookeeper.quorum",
HBaseAdmin admin=new HBaseAdmin(conf);
admin.disableTable("tab1".getBytes());
admin.deleteTable("tab1".getBytes());
admin.close();
public void testDeleteTable() throws Exception, IOException{
}