一、HBase简介
1.HBase概念
- HBase – Hadoop Database,是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统。
- 利用HBase技术可在廉价PC Server上搭建起大规模结构化存储集群。
- HBase利用Hadoop HDFS作为其文件存储系统,利用Hadoop MapReduce来处理HBase中的海量数据,利用Zookeeper作为协调工具。
2.HBase特点
- 大:一个表可以有数十亿行,上百万列;
- 无模式:每行都有一个可排序的主键和任意多的列,列可以根据需要动态的- 增加,同一张表中不同的行可以有截然不同的列;
- 面向列:面向列(族)的存储和权限控制,列(族)独立检索;
- 稀疏:对于空(null)的列,并不占用存储空间,表可以设计的非常稀疏;
- 数据多版本:每个单元中的数据可以有多个版本,默认情况下版本号自动分配,是单元格插入时的时间戳;
- 数据类型单一:HBase中的数据都是字符串,没有类型
3.引入HBase的原因
- 传统关系型数据库系统已无法适应大型分布式数据存储的需要
- 改良的关系数据库(副本、分区等)难于安装与维护
- 关系模型对数据的操作使数据的存贮变得复杂
- HBase从设计理念上就为可扩展做好了充分准备
- 空间的扩展只需要加入存储结点,使用‘表’的概念,但不同于关系数据库,HBase实质上是一张极大的、非常稀疏的,存储在分布式文件系统上的表(不支持SQL)
4.HBase数据模型
- 数据存放在带标签的表中
Tables 由 rows和columns组成. - Table cells单元格有版本
是HBase插入单元格时候的时间戳. - 列组成“列族”
所有的列族成员有相同的前缀. - 物理上,所有的列族成员都一起存放在文件系统中
HBase实际上就是一个面向列族的存储器 - HBase中的每一张表,就是所谓的BigTable。稀疏表。
- RowKey 和 ColumnKey 是二进制值byte[],按字典顺序排序
- Timestamp 是一个 64 位整数
- value 是一个未解释的字节数组byte[]
- 表中的不同行可以拥有不同数量的成员。即支持“动态模式”模型
HBase数据模型-行:
行的特点:
(1)字符串、整数、二进制串甚至串行化的结构都可以作为行键
(2)表按照行键的“逐字节排序”顺序对行进行有序化处理
(3)表内数据非常‘稀疏’,不同的行的列的数完全目可以大不相同
(4)可以只对一行上“锁”
(5)对行的写操作是始终是“原子”的
数据模型-列:
列的特点:
(1)列必须用‘族’(family)来定义
(2)任意一列有如下形式
1)“族:标签”
2)其中,族和标签都可为任意形式的串
(3)物理上将同“族”数据存储在一起
(4)数据可通过时间戳区分版本
5.HBase物理存储
二、HBase的shell操作
1.HBase shell介绍
- HBase提供了一个shell的终端给用户交互。我们称之 为:“HBase shell”。
- HBase Shell 提供了大多数的 HBase 命令, 通过 HBase Shell 用户可以方便地创建、删除及修改表, 还可以向表中添加数据、列出表中的相关信息等。
- 注意:写错 HBase Shell 命令时用键盘上的“Delete” 进行删除,“Backspace”不起作用。
2.HBase shell的启动
(1)启动HBase:start-hbase.sh
这将启动一个 HBase instance
(2)启动HBase shell:hbase shell
经过以上2步操作即可启动HBase shell,在命令行输入:help,即可查看 HBase shell支持的命令。
(3)退出HBase shell:quit
3.HBase shell常用命令
名称 | 命令表达式 |
---|---|
创建表 | create ‘表名称’, ‘列族名称1’,‘列族名称2’,‘列族名 称N’ |
添加记录 | put ‘表名称’, ‘行名称’, ‘列名称:’, ‘值’ |
查看记录 | get ‘表名称’, ‘行名称’ |
查看表中的记录总数 | count ‘表名称’ |
删除记录 | delete ‘表名’ ,‘行名称’ , ‘列名称’ |
删除一张表 | 先要屏蔽该表,才能对该表进行删除,第一步 disable ‘表名称’ 第二步 drop ‘表名称’ |
查看所有记录 | scan “表名称” |
查看某个表某个列中 所有数据 | scan “表名称” , {COLUMNS=>‘列族名称:列名称’} |
更新记录 | 就是重写一遍进行覆盖 |
列出全部表 | list |
得到表的描述 | describe ‘表名称’ |
查看表的状态 | exists ‘表名’;is_enabled ‘表名’;is_disabled ‘表名’ |
删除整行 | deleteall ‘表名’,‘行名’ |
清空表 | truncate ‘表名’ |
三、HBase组件、架构及运行原理
1.HBase基本组件
HMaste:
- 管理用户对Table的增、删、改、查操作
- 管理RegionServer的负载均衡、调整Region的分布
- 在Region Split后,将新Region分布到不同的RegionServer。
- 在RegionServer宕机后,该RegionServer上所管理的Region 由HMaster进行重新分配。
RegionServer:
- RegionServer是HBase集群运行在每个工作节点上的服务组件
- RegionServer维护Master分配给它的region,处理对这些region的IO请求
- Region server负责切分在运行过程中变得过大的region
Region:
- Region可理解为关系型数据库中的“分区”
- 处理RegionServer分配给它的任务
2.HBase架构
架构图:
-
Client:
包含访问HBase的接口,client维护着一些cache 来加快对HBase的访问,比如region的位置信息 -
Zookeeper:
(1)保证任何时候,集群中只有一个running master
(2)存贮所有Region的寻址入口
(3)实时监控Region Server 的状态,将Region server 的上线和下线信息,实时通知给Master
(4)存储HBase的schema,包括有哪些table,每个table有哪些column family -
Master:
可以启动多个HMaster,通过Zookeeper的Master Election机制保证总有一个Master运行
(1)为Region server 分配region
(2)负责region server 的负载均衡
(3)发现失效的region server 并重新分配其上的region -
Region Server:
(1)维护Master 分配给它的region,处理对这些 region 的 IO 请求
(2)负责切分在运行过程中变得过大的region
3.HBase组件单元
(1)表空间
- 在关系数据库系统中,命名空间namespace指的是一个表的逻辑分组,同一组中的表有类似的用途.
- 有两个系统内置的预定义命名空间(默认表空间):
hbase:系统命名空间,用于包含hbase的内部表
default:所有未指定命名空间的表都自动进入该命名空间
(2)表
HBase以表的形式存储数据,表在hdfs上以文件夹形式存
-
HBase表组成模型
1)RowKey:是Byte array,是表中每条记录的“主键”,方便快速查找
2)Column Family:列族,拥有一个名称(string),包含一个或者多个相关列
3)Column:属于某一个columnfamily,Version Number:类型为Long,默认值是系统时间戳,可由用户自定义
4)Value(Cell):Byte array -
HBase表的物理属性
1)Table中所有行都按照row key的字典序排列;
2)Table在行的方向上分割为多个Region;
3)Region按大小分割的,每个表开始只有一个region,随着数据增多,region不断增大,当增大到一个阀值的时候,region就会等分会两个新的region,之后会有越来越多的region;
4)Region是HBase中分布式存储和负载均衡的最小单元,不同Region分布到不同RegionServer上。
(3)列族
hbase表中的每个列,都归属与某个列族。每个column family存储在HDFS上的一个单独文件中,必须在使用表之前定义。列名都以列族作为前缀。例如info:name,info:age都属于info这个列族。
(4)列
在使用中动态添加
例如name,age是列,属于info这个列族
(5)RowKey
- Rowkey行键 (Rowkey)可以是任意字符串(最大长度是 64KB,实际应用中长度一般为 10-100bytes),在hbase内部,rowkey保存为字节数组,是hbase的key-value存储中的key。
- RowKey特性:rowkey按字典顺序的从小到大排序
(6)ColumnKey
二进制值byte[],按字典顺序排序。是表中每条记录的“主键”,方便快速查找
(7)时间戳( Timestamp )
时间戳,每次数据操作对应的时间戳,可以看作是数据的version number。是一个 64 位整数
(8)value
一个未解释的字节数组byte[]。
四、HBase数据写入与查询流程
1.HBase写入数据流程
(1)Client通过Zookeeper调度获取表的元数据信息;
(2)Cilent通过rpc协议与RegionServer交互,通过-ROOT-表与.META.表找到对应的对应的Region;
(3)将数据写入HLog日志中,如出现意外可以同通过HLog恢复信息;
(4)将数据写入Region的MemStore中,当MemStore达到阈值开始溢写,将其中的数据Flush成一个StoreFile;
(5)MemStore不断生成新的StoreFile,当StoreFile的数量到达阈值后会出发Compact合并操作,将多个StoreFile合并成一个StoreFile;
(6)StoreFile文件会不断增大,当达到阈值后会出发Split操作,把当前的Region且分为两个新的Region。父Region会下线,两个子Region会被HMaster分配到相应的RegionServer。
2.HBase查询数据流程
(1)Client访问Zookeeper,从ZK获取-ROOT-表的位置信息,通过访问-ROOT-表获取.META.表的位置,然后确定数据所在的HRegion位置;
(2)Client访问HRegion所在的HRegionServer,通过HRegionServer获取需要查找的数据;
(3)Client到HRegion的中去查找数据,首先到MemStore中查找,查到直接返回;查不到就去ClockCache中查找,查到直接返回;再查不到就去StoreFile中读数据,把读到的数据存入BlockCache中再返回Client。
五、HBase的API和工具类
1.HBase API 简介
HBase访问接口 | 说明 |
---|---|
Native Java API | 最常规和高效的访问方式,适合Hadoop MapReduce Job并行批处理HBase表数据 |
HBase Shell | HBase的命令行工具,最简单的接口,适合HBase管理使用 |
Thrift Gateway | 利用Thrift序列化技术,支持C++,PHP,Python等多种语言,适合其他异构系统在线访问HBase表数据 |
REST Gateway | 支持REST 风格的Http API访问HBase, 解除了语言限制 |
Pig | 可以使用Pig Latin流式编程语言来操作HBase中的数据,和Hive类似,本质最终也是编译成MapReduce Job来处理HBase表数据,适合做数据统计 |
Hive | 当前Hive的Release版本尚没有加入对HBase的支持,但在下一个版本Hive 0.7.0中将会支持HBase,可以使用类似SQL语言来访问HBase |
2.HBase的Java接口
示例代码:
package org.apache.hbase.hbase;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.MasterNotRunningException;
import org.apache.hadoop.hbase.ZooKeeperConnectionException;
import org.apache.hadoop.hbase.client.Delete;
import org.apache.hadoop.hbase.client.Get;
import org.apache.hadoop.hbase.client.HBaseAdmin;
import org.apache.hadoop.hbase.client.HTable;
import org.apache.hadoop.hbase.client.Put;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.client.Scan;
import org.apache.hadoop.hbase.util.Bytes;
public class hbase {
private static final String TABLE_NAME = "demo_table";
public static Configuration conf = null;
public HTable table = null;
public HBaseAdmin admin = null;
static {
conf = HBaseConfiguration.create();
System.out.println(conf.get("hbase.zookeeper.quorum"));
}
/**
* 创建一张表
*/
public static void creatTable(String tableName, String[] familys)
throws Exception {
HBaseAdmin admin = new HBaseAdmin(conf);
if (admin.tableExists(tableName)) {
System.out.println("table already exists!");
} else {
HTableDescriptor tableDesc = new HTableDescriptor(tableName);
for (int i = 0; i < familys.length; i++) {
tableDesc.addFamily(new HColumnDescriptor(familys[i]));
}
admin.createTable(tableDesc);
System.out.println("create table " + tableName + " ok.");
}
}
/**
* 删除表
*/
public static void deleteTable(String tableName) throws Exception {
try {
HBaseAdmin admin = new HBaseAdmin(conf);
admin.disableTable(tableName);
admin.deleteTable(tableName);
System.out.println("delete table " + tableName + " ok.");
} catch (MasterNotRunningException e) {
e.printStackTrace();
} catch (ZooKeeperConnectionException e) {
e.printStackTrace();
}
}
/**
* 插入一行记录
*/
public static void addRecord(String tableName, String rowKey,
String family, String qualifier, String value) throws Exception {
try {
HTable table = new HTable(conf, tableName);
Put put = new Put(Bytes.toBytes(rowKey));
put.add(Bytes.toBytes(family), Bytes.toBytes(qualifier),
Bytes.toBytes(value));
table.put(put);
System.out.println("insert recored " + rowKey + " to table "
+ tableName + " ok.");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 删除一行记录
*/
public static void delRecord(String tableName, String rowKey)
throws IOException {
HTable table = new HTable(conf, tableName);
List list = new ArrayList();
Delete del = new Delete(rowKey.getBytes());
list.add(del);
table.delete(list);
System.out.println("del recored " + rowKey + " ok.");
}
/**
* 查找一行记录
*/
public static void getOneRecord(String tableName, String rowKey)
throws IOException {
HTable table = new HTable(conf, tableName);
Get get = new Get(rowKey.getBytes());
Result rs = table.get(get);
for (KeyValue kv : rs.raw()) {
System.out.print(new String(kv.getRow()) + " ");
System.out.print(new String(kv.getFamily()) + ":");
System.out.print(new String(kv.getQualifier()) + " ");
System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
}
}
/**
* 显示所有数据
*/
public static void getAllRecord(String tableName) {
try {
HTable table = new HTable(conf, tableName);
Scan s = new Scan();
ResultScanner ss = table.getScanner(s);
for (Result r : ss) {
for (KeyValue kv : r.raw()) {
System.out.print(new String(kv.getRow()) + " ");
System.out.print(new String(kv.getFamily()) + ":");
System.out.print(new String(kv.getQualifier()) + " ");
System.out.print(kv.getTimestamp() + " ");
System.out.println(new String(kv.getValue()));
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
String tablename = "201712320**";
String[] familys = { "grade", "course" };
hbase.creatTable(tablename, familys);
// add record wsn001
hbase.addRecord(tablename, "wsn001", "grade", "", "大三");
hbase.addRecord(tablename, "wsn001", "course", "分布式数据库", "100");
hbase.addRecord(tablename, "wsn001", "course", "虚拟化原理", "100");
hbase.addRecord(tablename, "wsn001", "course", "大数据处理技术", "100");
// add record wsn102
hbase.addRecord(tablename, "wsn102", "grade", "", "大四");
hbase.addRecord(tablename, "wsn102", "course", "软件工程", "100");
System.out.println("===========get one record========");
hbase.getOneRecord(tablename, "wsn001");
System.out.println("===========show all record========");
hbase.getAllRecord(tablename);
System.out.println("===========del one record========");
hbase.delRecord(tablename, "wsn102");
hbase.getAllRecord(tablename);
System.out.println("===========show all record========");
hbase.getAllRecord(tablename);
} catch (Exception e) {
e.printStackTrace();
}
}}
3.HBase的工具类
HTable是一个比较重的对象,比如加载配置文件,连接ZK,查询meta表等等,高并发的时候影响系统的性能,因此引入了“池”的概念
HTablePool定义:
public class TableConnection {
private TableConnection(){
}
private static HConnection connection =null;
public static HConnection getConnection(){
if(connection==null){
ExecutorService pool = Executors.newFixedThreadPool(10);
Configuration conf = HBaseConfiguration.create();
conf.set("hbase.zookeeper.quorum", "192.168.1.81:2181,192.168.1.82:2181");
try {
connection=HConnectionManager.createConnection(conf,pool);
} catch (IOException e) {
}
}
return connection;
}
}
使用:
Put put =new Put(Bytes.toBytes("row"));
TableConnection.getConnection().getTable("tableName").put(put);
4.HBase过滤器
过滤器筛选的数据能够细化到具体的一个存储单元格上(由行键,列明,时间戳定位),多条件查询。过滤器在服务器端执行。
六、HBase调优
1.Rowkey优化的主要方式
(1)生成随机数、hash、散列值
(2)字符串反转
(3)字符串拼接
2.HBase优化分配堆内存
HBase 操作过程中需要大量的内存开销,毕竟 Table 是可以缓存在内存中的,一般会分配整个可用内存的 70%给 HBase 的 Java 堆。但是不建议分配非常大的堆内存,因为 GC 过 程持续太久会导致 RegionServer 处于长期不可用状态,一般 16~48G 内存就可以了,如果因 为框架占用内存过高导致系统内存不足,框架一样会被系统服务拖死。