目录
1 Hbase概述
1.1 什么是Hbase
1.2 Hbase特点
1.3HBase架构
1.4HBase中的角色
1.4.1 HMaster
1.4.2 RegionServer
1.4.3 其他组件
2 Hbase安装
2.1 环境准备
2.2 下载安装
2.3 Hbase服务的启动
2.4 查看Hbase的WEBUI界面
3 Hbase Shell操作
3.1 基本操作
3.2 表的操作
4 Hbase数据结构
4.1 RowKey
4.2 Column Family
4.3 Time Stamp
4.4命名空间
5 Hbase原理
6 HbaseAPI操作
6.1 HbaseAPI
7 Hbase优化
7.1 高可用(high available)
7.2RowKey设计
7.3预分区
7.4 内存优化
1 Hbase概述
1.1 什么是Hbase
HBase的原型是Google的BigTable论文,受到了该论文思想的启发,目前作为Hadoop的子项目来开发维护。
官方网站:http://hbase.apache.org
-- 2006年Google发表BigTable白皮书
-- 2006年开始开发HBase
-- 2008年北京成功开奥运会,程序员默默地将HBase弄成了Hadoop的子项目
-- 2010年HBase成为Apache顶级项目
-- 现在很多公司二次开发出了很多发行版本,你也开始使用了。
->HBase是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统,利用HBASE技术可在廉价PC Server上搭建起大规模结构化存储集群。
->HBase的目标是存储并处理大型的数据,更具体来说是仅需使用普通的硬件配置,就能够处理由成千上万的行和列所组成的大型数据。
HBase是Google Bigtable的开源实现,但是也有很多不同之处。比如:Google Bigtable利用GFS作为其文件存储系统,HBase利用Hadoop HDFS作为其文件存储系统;Google运行MAPREDUCE来处理Bigtable中的海量数据,HBase同样利用Hadoop MapReduce来处理HBase中的海量数据;Google Bigtable利用Chubby作为协同服务,HBase利用Zookeeper作为对应。
Zk:ha使用过自动故障转移;kafka存储元数据(集群的元数据+消费者的元数据)
1.2 Hbase特点
1)海量存储
Hbase适合存储PB级别的海量数据,在PB级别的数据以及采用廉价PC存储的情况下,能在几十到百毫秒内返回数据。这与Hbase的极易扩展性息息相关。正是因为Hbase良好的扩展性,才为海量数据的存储提供了便利。
2)列式存储
->这里的列式存储其实说的是列族存储,Hbase是根据列族来存储数据的。列族下面可以有非常多的列,列族在创建表的时候就必须指定。
3)极易扩展
Hbase的扩展性主要体现在两个方面,一个是基于上层处理能力(RegionServer)的扩展,一个是基于存储的扩展(HDFS)。
->通过横向添加RegionSever的机器,进行水平扩展,提升Hbase上层的处理能力,提升Hbsae服务更多Region的能力。
备注:RegionServer的作用是管理region、承接业务的访问,这个后面会详细的介绍,通过横向添加Datanode的机器,进行存储层扩容,提升Hbase的数据存储能力和提升后端存储的读写能力。
4)高并发
由于目前大部分使用Hbase的架构,都是采用的廉价PC,因此单个IO的延迟其实并不小,一般在几十到上百ms之间。这里说的高并发,主要是在并发的情况下,Hbase的单个IO延迟下降并不多。能获得高并发、低延迟的服务。
5)稀疏
稀疏主要是针对Hbase列的灵活性,在列族中,你可以指定任意多的列,在列数据为空的情况下,是不会占用存储空间的。
1.3HBase架构
从图中可以看出Hbase是由Client、Zookeeper、Master、HRegionServer、HDFS等几个组件组成,下面来介绍一下几个组件的相关功能:
1)Client
Client包含了访问Hbase的接口,另外Client还维护了对应的cache来加速Hbase的访问,比如cache的.META.元数据的信息。
2)Zookeeper
HBase通过Zookeeper来做master的高可用、RegionServer的监控、元数据的入口以及集群配置的维护等工作。具体工作如下:
通过Zoopkeeper来保证集群中只有1个master在运行,如果master异常,会通过竞争机制产生新的master提供服务
通过Zoopkeeper来监控RegionServer的状态,当RegionSevrer有异常的时候,通过回调的形式通知Master RegionServer上下线的信息(就是使用zk完成的服务器上下线机制)
通过Zoopkeeper存储元数据的统一入口地址
3)Hmaster
master节点的主要职责如下:
(1)为RegionServer分配Region
(2)维护整个集群的负载均衡
(3)维护集群的元数据信息
(4)发现失效的Region,并将失效的Region分配到正常的RegionServer上
(5)当RegionSever失效的时候,协调对应Hlog的拆分
4)HregionServer
HregionServer直接对接用户的读写请求,是真正的“干活”的节点。它的功能概括如下:
(1)管理master为其分配的Region
(2)处理来自客户端的读写请求
(3)负责和底层HDFS的交互,存储数据到HDFS
(4)负责Region变大以后的拆分
(5)负责Storefile的合并工作
5)HDFS
HDFS为Hbase提供最终的底层数据存储服务,同时为HBase提供高可用(Hlog存在HDFS)的支持,具体功能概括如下:
(1)提供元数据和表数据的底层分布式存储服务
(2)数据多副本,保证高可靠和高可用性
1.4HBase中的角色
1.4.1 HMaster
功能
- 监控RegionServer
- 处理RegionServer故障转移
- 处理元数据的变更
- 处理region的分配或转移
- 在空闲时间进行数据的负载均衡
- 通过Zookeeper发布元数据的位置给客户端
1.4.2 RegionServer
功能
- 负责存储HBase的实际数据
- 处理分配给它的Region
- 刷新缓存到HDFS
- 维护Hlog(每个regionserver都有一个Hlog)
- 执行压缩
- 负责处理Region分片(自动分片)
1.4.3 其他组件
1.Write-Ahead logs(WAL,HLOG&edits日志)
HBase的修改记录,当对HBase读写数据的时候,数据不是直接写进磁盘,它会在内存中保留一段时间(时间以及数据量阈值可以设定)。但把数据保存在内存中可能有更高的概率引起数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile的文件中,然后再写入内存中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。
2.Region
Hbase表的分片,HBase表会根据RowKey值被切分成不同的region存储在RegionServer中,在一个RegionServer中可以有多个不同的region。
3.Store
HFile存储在Store中,一个Store对应HBase表中的一个列族。
4.MemStore
顾名思义,就是内存存储,位于内存中,用来保存当前的数据操作,所以当数据保存在WAL中之后,RegsionServer会在内存中存储键值对。
5.HFile
这是在磁盘上保存原始数据的实际的物理文件,是实际的存储文件。StoreFile是以HFile的形式存储在HDFS的。
2 Hbase安装
2.1 环境准备
(1)zookeeper正常部署:保证zookeeper的正常部署,并启动zookeeper集群
(2)Hadoop正常部署:保证Hadoop集群的正常部署,并启动hdfs和yarn
2.2 下载安装
1)下载:
官方网站:http://hbase.apache.org/downloads.html
网盘链接:请点这里 提取码:cgf7
2)上传并安装
(1)将现在的Hbase安装包上传到hadoop101节点/opt/modoule/目录下,配置完成后再分发到其他节点
(2)解压Hbase到指定目录:
[root@hadoop101 software]# tar -zxvf hadoop-2.7.2.tar.gz -C /opt/module/
(3)修改conf目录下的配置文件:
1)修改hbase-env.sh,添加以下内容:
export JAVA_HOME=/opt/module/jdk1.8.0_144
export HBASE_MANAGES_ZK=false
2)hbase-site.xml修改内容如下:
<configuration>
<property>
<name>hbase.rootdir</name>
<value>hdfs://hadoop101:9000/hbase</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
<!--0.98后的新变动,之前版本没有port,默认端口为60000 -->
<property>
<name>hbase.master.port</name>
<value>16000</value>
</property>
<property>
<name>hbase.zookeeper.quorum</name>
<value>hadoop101:2181,hadoop102:2181,hadoop103:2181</value>
</property>
<property>
<name>hbase.zookeeper.property.dataDir</name>
<value>/opt/module/zookeeper-3.4.10/zkData</value>
</property>
</configuration>
3)修改regionservers:
hadoop101
hadoop102
hadoop103
4)软连接hadoop配置文件到hbase:
[root@hadoop101 module]$ ln -s /opt/module/hadoop-2.7.2/etc/hadoop/core-site.xml /opt/module/hbase-1.3.1/conf/core-site.xml
[root@hadoop101 module]$ ln -s /opt/module/hadoop-2.7.2/etc/hadoop/hdfs-site.xml /opt/module/hbase-1.3.1/conf/hdfs-site.xml
5)将配置好的Hbase分发到其他节点
2.3 Hbase服务的启动
1)启动方式1:
[root@hadoop101 hbase]$ bin/hbase-daemon.sh start master
[root@hadoop101 hbase]$ bin/hbase-daemon.sh start regionserver
可能出现的问题:如果集群之间的节点时间不同步,会导致regionserver无法启动,抛出ClockOutOfSyncException异常。
解决方案:
a、同步时间服务
b、属性:hbase.master.maxclockskew设置更大的值
<property>
<name>hbase.master.maxclockskew</name>
<value>180000</value>
<description>Time difference of regionserver from master</description>
</property>
2)启动方式2:
[root@hadoop101 hbase]$ bin/start-hbase.sh
[root@hadoop101 hbase]$ bin/stop-hbase.sh //对应的停止服务
2.4 查看Hbase的WEBUI界面
启动成功后,可以通过“host:port”的方式来访问HBase管理页面,例如:http://hadoop101:16010
3 Hbase Shell操作
3.1 基本操作
1.进入HBase客户端命令行(任意节点均可)
[root@hadoop101 hbase]$ bin/hbase shell
2.查看帮助命令
hbase(main):001:0> help
3.查看当前数据库中有哪些表
hbase(main):002:0> list
3.2 表的操作
1.创建表
hbase(main):002:0> create 'student','info' (单双引号无所谓)
2.插入数据到表
hbase(main):003:0> put 'student','1001','info:sex','male'
hbase(main):004:0> put 'student','1001','info:age','18'
hbase(main):005:0> put 'student','1002','info:name','Janna'
hbase(main):006:0> put 'student','1002','info:sex','female'
hbase(main):007:0> put 'student','1002','info:age','20'
3.扫描查看表数据
hbase(main):008:0> scan 'student'
hbase(main):009:0> scan 'student',{STARTROW => '1001', STOPROW => '1002'}
hbase(main):010:0> scan 'student',{STARTROW => '1001'}
4.查看表结构
hbase(main):011:0> describe 'student'
5.更新指定字段的数据
hbase(main):012:0> put 'student','1001','info:name','Nick'
hbase(main):013:0> put 'student','1001','info:age','100'
6.查看“指定行”或“指定列族:列”的数据
hbase(main):014:0> get 'student','1001'
hbase(main):015:0> get 'student','1001','info:name'
7.统计表数据行数
hbase(main):021:0> count 'student'
8.删除数据
删除某rowkey的全部数据:
hbase(main):016:0> deleteall 'student','1001'
删除某rowkey的某一列数据:
hbase(main):017:0> delete 'student','1002','info:sex'
9.清空表数据(表结构还在)
hbase(main):018:0> truncate 'student'
提示:清空表的操作顺序为先disable,然后再truncate。
10.删除表
首先需要先让该表为disable状态:
hbase(main):019:0> disable 'student'
然后才能drop这个表:
hbase(main):020:0> drop 'student'
提示:如果直接drop表,会报错:ERROR: Table student is enabled. Disable it first.
11.变更表信息
将info列族中的数据存放3个版本:
hbase(main):022:0> alter 'student',{NAME=>'info',VERSIONS=>3}
hbase(main):022:0>get 'student','1001',{COLUMN=>'info:name',VERSIONS=>3}
4 Hbase数据结构
4.1 RowKey
与nosql数据库们一样,RowKey是用来检索记录的主键。访问HBASE table中的行,只有三种方式:
- 通过单个RowKey访问
- 通过RowKey的range
- 全表扫描
RowKey行键 (RowKey)可以是任意字符串(最大长度是64KB,实际应用中长度一般为 10-100bytes),在HBASE内部,RowKey保存为字节数组。存储时,数据按照RowKey的字典序(byte order)排序存储。设计RowKey时,要充分利用排序存储这个特性,将经常一起读取的行存储放到一起。(位置相关性)
4.2 Column Family
列族:HBASE表中的每个列,都归属于某个列族。列族是表的schema的一部分(而列不是),必须在使用表之前定义。列名都以列族作为前缀。例如 courses:history,courses:math都属于courses 这个列族。
4.3 Time Stamp
HBASE 中通过rowkey和columns确定的为一个存贮单元称为cell。cell中的数据是没有类型的,全部是字节数组形式存贮。每个 cell都保存 着同一份数据的多个版本。版本通过时间戳来索引。时间戳的类型是 64位整型。时间戳可以由HBASE(在数据写入时自动 )赋值,此时时间戳是精确到毫秒 的当前系统时间。时间戳也可以由客户显式赋值。如果应用程序要避免数据版 本冲突,就必须自己生成具有唯一性的时间戳。每个 cell中,不同版本的数据按照时间倒序排序,即最新的数据排在最前面。
为了避免数据存在过多版本造成的的管理 (包括存贮和索引)负担,HBASE提供 了两种数据版本回收方式。一是保存数据的最后n个版本,二是保存最近一段 时间内的版本(比如最近七天)。用户可以针对每个列族进行设置。
4.4命名空间
命名空间的结构:
1) Table:表,所有的表都是命名空间的成员,即表必属于某个命名空间,如果没有指定,则在default默认的命名空间中。
2) RegionServergroup:一个命名空间包含了默认的RegionServerGroup。
3) Permission:权限,命名空间能够让我们来定义访问控制列表ACL(Access Control List)。例如,创建表,读取表,删除,更新等等操作。
4) Quota:限额,可以强制一个命名空间可包含的region的数量。
5 Hbase原理
可以参考我的另一篇文章:https://blog.csdn.net/jiezou12138/article/details/88677006
6 HbaseAPI操作
首先添加下面的依赖:
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.3.1</version>
</dependency>
6.1 HbaseAPI
package com.bigdata;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.*;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Before;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class HbaseTest {
private Configuration conf;
@Before
public void init() {
conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "192.168.1.102");
conf.set("hbase.zookeeper.property.clientPort", "2181");
}
/**
* use_case:判断Hbase表是否存在
* @throws IOException
*/
@Test
public void testExists() throws IOException {
Connection conn = ConnectionFactory.createConnection(conf);
HBaseAdmin hBaseAdmin = (HBaseAdmin) conn.getAdmin();
boolean exists = hBaseAdmin.tableExists(TableName.valueOf("student"));
System.out.println("=========================>>>" + exists);
hBaseAdmin.close();
conn.close();
}
/**
* use_case:创建Hbase表
*/
@Test
public void testCreateTable() throws IOException {
//1.获取连接对象
Connection connection = ConnectionFactory.createConnection(conf);
//2.通过连接获取Hbase的客户端对象
HBaseAdmin hBaseAdmin = (HBaseAdmin) connection.getAdmin();
//3.创建表描述器
HTableDescriptor student = new HTableDescriptor(TableName.valueOf("student1"));
//4.设置列组描述器
student.addFamily(new HColumnDescriptor("info"));
//5.执行创建操作
hBaseAdmin.createTable(student);
System.out.println("====================>>>student表创建成功了");
hBaseAdmin.close();
connection.close();
}
/**
* use_case:删除hbase表
*/
@Test
public void testDeleteTable() throws IOException {
//1.获取连接对象
Connection conn = ConnectionFactory.createConnection(conf);
//2.通过连接对象获取Hbase的客户端对象
HBaseAdmin admin = (HBaseAdmin) conn.getAdmin();
//3.先设置表位不可用
admin.disableTable("student1");
//4.删除表
admin.deleteTable("student1");
admin.close();
conn.close();
}
/**
* use_case:向表中插入一条数据
*/
@Test
public void testInsert() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
//获取一个表对象
Table student = connection.getTable(TableName.valueOf("student"));
//设置rowkey
Put put = new Put(Bytes.toBytes("1106"));
//设置列组、列、列值
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("Rose"));
//执行插入
student.put(put);
student.close();
admin.close();
connection.close();
}
/**
* use_case:向表中插入多条数据
*/
@Test
public void testInserts() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
//获取一个表对象
Table student = connection.getTable(TableName.valueOf("student"));
List<Put> list = new ArrayList<>();
Put put1 = new Put(Bytes.toBytes("1106"));
put1.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"), Bytes.toBytes("Rose"));
Put put2 = new Put(Bytes.toBytes("1106"));
put2.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("16"));
Put put3 = new Put(Bytes.toBytes("1106"));
put3.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"), Bytes.toBytes("female"));
list.add(put1);
list.add(put2);
list.add(put3);
student.put(list);
student.close();
admin.close();
connection.close();
}
/**
* use_case:删除一行或多行数据
*/
@Test
public void testDelete() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
Table student = connection.getTable(TableName.valueOf("student"));
Delete delete = new Delete(Bytes.toBytes("1106"));
Delete delete1 = new Delete(Bytes.toBytes("1107"));
List<Delete> list = new ArrayList<>();
list.add(delete);
list.add(delete1);
student.delete(list);
student.close();
admin.close();
connection.close();
}
/**
* use_case:获取所有数据
*/
@Test
public void testScan() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
Table student = connection.getTable(TableName.valueOf("student"));
ResultScanner scanner = student.getScanner(new Scan());
for (Result result : scanner) {
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
String row = Bytes.toString(CellUtil.cloneRow(cell));
String cf = Bytes.toString(CellUtil.cloneFamily(cell));
String column = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneva lue(cell));
System.out.println("row:" + row + " column:" + row + ":" + column + " value=" + value);
}
}
student.close();
admin.close();
connection.close();
}
/**
* use_case:获取某一行的数据,指定列族,列
*/
@Test
public void testGet() throws IOException {
Connection connection = ConnectionFactory.createConnection(conf);
Table student = connection.getTable(TableName.valueOf("student"));
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
//创建查询的get对象
Get get = new Get(Bytes.toBytes("1105"));
//指定要查询的列族的指定列
//get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("name"));
//Result result = student.get(get);
//查询指定的列族
get.addFamily(Bytes.toBytes("info"));
Result result = student.get(get);
Cell[] cells = result.rawCells();
for (Cell cell : cells) {
String row = Bytes.toString(CellUtil.cloneRow(cell));
String cf = Bytes.toString(CellUtil.cloneFamily(cell));
String column = Bytes.toString(CellUtil.cloneQualifier(cell));
String value = Bytes.toString(CellUtil.cloneva lue(cell));
System.out.println("row:" + row + " column:" + row + ":" + column + " value=" + value);
}
student.close();
admin.close();
connection.close();
}
}
7 Hbase优化
7.1 高可用(high available)
在HBase中HMaster负责监控RegionServer的生命周期,均衡RegionServer的负载,如果HMaster挂掉了,那么整个HBase集群将陷入不健康的状态,并且此时的工作状态并不会维持太久。所以HBase支持对HMaster的高可用配置。
1.关闭HBase集群(如果没有开启则跳过此步)
[root@hadoop101 hbase-1.3.1]$ bin/stop-hbase.sh
2.在conf目录下创建backup-masters文件
[root@hadoop101 hbase-1.3.1]$ touch conf/backup-masters
3.在backup-masters文件中配置高可用HMaster节点
[root@hadoop101 hbase-1.3.1]$ echo hadoop103 > conf/backup-masters
4.将backup-masters分发到其他节点
5.启动集群并打开页面测试查看
7.2RowKey设计
一条数据的唯一标识就是rowkey,那么这条数据存储于哪个分区,取决于rowkey处于哪个一个预分区的区间内,设计rowkey的主要目的 ,就是让数据均匀的分布于所有的region中,在一定程度上防止数据倾斜。接下来我们就谈一谈rowkey常用的设计方案。
注意:RowKey如何设计必须结合实际业务场景
设计原则可参考下面两篇博文:
https://www.cnblogs.com/yuguoshuo/p/6265649.html
https://blog.csdn.net/b6ecl1k7BS8O/article/details/82754169
7.3预分区
每一个region维护着startRowKey与endRowKey,如果加入的数据符合某个region维护的rowKey范围,则该数据交给这个region维护。那么依照这个原则,我们可以将数据所要投放的分区提前大致的规划好,以提高HBase性能。
注意:手动分区(预分区)需要对业务数据量有把控
1.手动设定预分区
hbase> create 'staff1','info',SPLITS => ['1000','2000','3000','4000']
7.4 内存优化
HBase操作过程中需要大量的内存开销,毕竟Table是可以缓存在内存中的,一般会分配整个可用内存的70%给HBase的Java堆。但是不建议分配非常大的堆内存,因为GC过程持续太久会导致RegionServer处于长期不可用状态,一般16~48G内存就可以了,如果因为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。