HBase
HBase简介
面向列、适合非结构化数据存储
HBase是一个分布式的、面向列的开源数据库,该技术来源于 Fay Chang 所撰写的Google论文“Bigtable:一个结构化数据的分布式存储系统”。就像Bigtable利用了Google文件系统(File System)所提供的分布式数据存储一样,HBase在Hadoop之上提供了类似于Bigtable的能力。HBase是Apache的Hadoop项目的子项目。HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库。另一个不同的是HBase基于列的而不是基于行的模式
在生态圈所处地位:
- HBase是Apache基金会顶级项目
- HBase基于Hadoop的核心HDFS系统进行数据存储
- HBase可以存储超大数据并使用用来进行大数据的==实时查询==
HBase & HDFS
- HBase建立在Hadoop文件系统之上,利用了Hadoop文件系统的容错能力
- HBase提供对数据的随机实时读写/访问的能力
- HBase内部使用哈希表,并存储索引,可将HDFS文件中数据进行快速查找
HBase & 常用关系型数据库
事物支持 只支持单个Row级别,索引只支持Row-key
- NameSpace: 可以把NameSpace理解为RDBMS的数据库
- Table: 表名必须是能用在文件路径里的合法名字
- Row: 在表里面,每一行代表着一个数据对象,每一行都是以一个行键(Row Key)来进行唯一标识的,行键并没有什么特定的数据类型,以二进制的字节来存储
- Column: HBase的列由Column family和Column qualifier组成,由冒号(:)进行间隔。比如famiy:qualifier
- RowKey: 可以唯一标识一行记录,不可被改变
- Column Family: 在定义HBase表的时候需要提前设置好列族,表中所有列都需要组织在列族里面
- Column Qualifier: 列族中的数据通过列标识来进行映射,可以理解为一个键值对,Column Qualifier就是key
- Cell: 每一个行键,列族和列标识共同组成一个单元
- Timestamp: 每一个值都会有一个timestamp,作为该值特定版本的标识符,由HBase管理,默认保存3个保本
关系型数据库存储方式:
ID | File Name | File Size | creator |
---|---|---|---|
1 | a.txt | 20 | 张三 |
2 | b.txt | 30 | 李四 |
HBase存储:
将File Name和File Size抽象成一个列族,将creator抽象成一个列族
RowKey | FileInfo | SaveInfo |
---|---|---|
1 | name:a.txt size:20 | creator:张三 |
2 | name:b.txt size:30 | creator:李四 |
使用场景
- 瞬间写入量很大,常用数据不好支撑或需要很高成本
- 数据需要长久保存,且量会持久增长到比较大的场景
- HBase不适用于有join,多级索引,表关系复制的数据模型
HBase安装
- 下载对应安装包,找到对应Hadoop版本下载,并解压
- 复制 Hadoop内 core.site.xml和hdfs-site.xml文件到HBase的conf目录内
- 修改hbase-env.sh
- export JAVA_HOME=/home/hadoop/app/jdk8
- 找到 Configure PermSize. Only needed in JDK7 … 注释掉下面的两行(HBASE_MASTER_OPTS 、HBASE_REGIONSERVER_OPTS)
- 修改 hbase-site.xml
<configuration>
<property>
<name>hbase.rootdir</name>
# 和hadoop启动的端口一致
<value>hdfs://hadoop000:8020/hbase</value>
</property>
<property>
# 新建存储目录
<name>hbase.zookeeper.property.dataDir</name>
<value>/home/hadoop/app/hbasetmp</value>
</property>
<property>
<name>hbase.cluster.distributed</name>
<value>true</value>
</property>
</configuration>
HBASE读写流程
HBASE 写流程
- Client会先访问zookeeper,得到对应的RegionServer地址
- Client对RegionServer发起写请求,RegionServer接受数据写入内存
- 当MemStrore的大小达到一定的值后,flush到StoreFile并存储到HDFS
名次释意:
- RegionServer : 管理多个 Region, 每一个RegionServer对应一个HLog
- Region : 包含多个Store,每一个Store 包含 MemStore 和 StoreFile,写入数据的时候,先写入到MemStore中,当MemStore满了, 再写入到 StoreFile
- StoreFile : 对应HDFS中的HFile,是其一层封装
- WAL : Write-Ahead Logging, 预写日志系统 数据库中一种高效的日志算法,对于非内存数据库而言,磁盘I/O操作是数据库效率的一大瓶颈。在相同的数据量下,采用WAL日志的数据库系统在事务提交时,磁盘写操作只有传统的回滚日志的一半左右,大大提高了数据库磁盘I/O操作的效率,从而提高了数据库的性能。 一般的WAL的实现方式是先写入日志,再写入内存,HBase是先写入内存,再写到HLog中(更新操作)
- HLog : WAL的一种实现方式,只有当HLog更新了,才表示此条数据写入成功。
HBASE 读流程
- Client会先访问zookeeper,得到对应的RegionServer地址,拉去meta信息表
- Client对RegionServer发起读请求,RegionServer根据meta信息开始读取数据
- 扫描memStore –> 扫描blockcache – > 从storefile中读取数据
三个问题
- HBase启动时发生了什么
- HMaster启动,注册Zookeeper,等待RegionServer汇报
- RegionServer注册到Zokeeper,并向HMaster汇报
- 对各个RegionServer(包括失效的)的数据进行数据整理,分配Region和meta信息
- 其他backupMaster定期向activeMaster进行更新,保证meta的最新
- 当RegionServer失效后会发生什么
- HMaster将失效的RegionServer上的Region分配到其他节点
- HMaster更新 hbase:meta 表以保证数据正常访问
- 当HMaster失效后会发生什么
- 处于Backup状态的HMaster节点推选出一个转为Active状态
- (若没有配置高可用,activemaster失效后)数据能正常读写,但是不能创建删除表,也不能更改表结构
HBase实战使用
SHELL操作命令
名称 | 命令表达式 |
---|---|
查看存在哪些表 | list |
创建表 | create ‘表名称’,’列名称1’,’列名称2’,’列名称N’ |
添加记录 | put ‘表名称’,’行名称’,’列名称’,’值’ |
查看记录 | get ‘表名称’,’行名称’ |
查看表中的记录总数 | count ‘表名称’ |
删除记录 | delete ‘表名’,’行名称’,’列名称’ |
删除一张表 | 先要屏蔽该表,才能对该表进行删除,第一步 disable ‘表名称’ 第二步 drop ‘表名称’ |
查看所有记录 | scan ‘表名称’ |
查看某个表某个列中的所有数据 | scan ‘表名称’,[‘列名称:’] |
更新记录 | 就是重写一边进行覆盖 |
进入Hbase命令行操作 : $HBASE_HOME/bin/hbase.sh shell
查看集群状态 : status
查看所有的表 : list
创建一张表并制定列族 : create 'FileTable','saveInfo','tableInfo'
添加一个列族 : alter 'FileTable','cf'
删除一个列族 : alter 'Filetable',{NAME=>'cf',METHOD=>'delete'}
添加数据 : put 'FileTable','rowkey1','fileInfo:size','10241'
查看表数据 : get 'FileTable','rowkey1'
查看指定列族 : get 'FileTable','rowkey1','fileInfo'
查看所有数据 : scan 'FileTable'
查看指定列 : scan 'FileTable',{COLUMN=>'fileinfo'}
查看 从 rowkey1开始,返回2条数据,版本是最行的
scan 'FileTable',{STARTROW=>'rokey1',LIMIT=>2,VERSIONS=>1}
删除列中某一个值 : delete 'FileTable','rowkey1','fileInfo:size'
删除整个列 : delteall 'FileTable','rowkey1'
删除整个表 :
1. disable 'FileTable'
2. drop 'FileTable'
查看表状态 is_dsiabled 'FileTable'
查看是否存在 exists 'FileTable'
HBase JAVA API
开发HBase数据库操作类
过滤器
HBase为筛选数据提供了一组过滤器,通过过滤器可以在数据的 多个纬度上进行数据的筛选(行、列、数据版本),通常来说对行 列进行筛选的应用场景较多
- 基于行的过滤器
- PrefixFilter: 行的前缀匹配
- PageFilter: 基于行的分页
- 基于列的过滤器
- ColumnPrefixFilter: 列前缀匹配
- FirstKeyOnlyFilter: 只返回每一行的第一列
- 基于单元值的过滤器
- KeyOnlyFilter: 返回的数据不包括单元值,只包含行键与列
- TimestampsFilter: 根据数据的时间戳版本进行过滤
- 基于列和单元值的过滤器
- SingleColumnValueFilter : 对该列的单元值进行比较过滤
- SingleColumnValueExcludeFilter: 对该列的单元值进行比较过滤
- 比较过滤器
- 比较过滤器通常需要一个比较运算符以及一个比较器来实现过滤
- RowFilter、FamilyFilter、QualifierFilter、ValueFilter
- 自定义过滤器
- 实现FilterBase类 实现相应方法
简单使用
引入包
<dependencies>
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.2.4</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
获取数据库连接
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.Connection;
import org.apache.hadoop.hbase.client.ConnectionFactory;
import org.apache.hadoop.hbase.client.Table;
import java.io.IOException;
public class HBaseConn {
private static final HBaseConn INSTANCE = new HBaseConn();
private static Configuration configuration;
private static Connection connection;
private HBaseConn(){
try {
if (configuration == null){
configuration = HBaseConfiguration.create();
// zookeeper 地址及端口
configuration.set("hbase.zookeeper.quorum","172.16.54.134:2181");
}
}catch (Exception e){
e.printStackTrace();
}
}
// 获取连接
private Connection getConnection(){
if (connection == null || connection.isClosed()){
try {
// 设置连接配置
connection = ConnectionFactory.createConnection(configuration);
}catch (Exception e){
e.printStackTrace();
}
}
return connection;
}
// 获取连接
public static Connection getHBaseConn(){
return INSTANCE.getConnection();
}
// 获取表
public static Table getTable(String tableName) throws IOException {
return INSTANCE.getConnection().getTable(TableName.valueOf(tableName));
}
// 关闭连接
public static void closeConn(){
if (connection != null){
try {
connection.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
创建相应方法
import org.apache.hadoop.hbase.HColumnDescriptor;
import org.apache.hadoop.hbase.HTableDescriptor;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.filter.FilterList;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
public class HBaseUtil {
/**
* @description:
* @date: 2019/07/17
* @param: [tableName, cfs]
* 表名 列族的数组
* @return: boolean
*/
public static boolean createTable(String tableName,String[] cfs){
try (HBaseAdmin admin= (HBaseAdmin)HBaseConn.getHBaseConn().getAdmin()) {
if (admin.tableExists(tableName)){
return false;
}
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
Arrays.stream(cfs).forEach( cf -> {
HColumnDescriptor columnDescriptor = new HColumnDescriptor(cf);
columnDescriptor.setMaxVersions(1);
tableDescriptor.addFamily(columnDescriptor);
});
admin.createTable(tableDescriptor);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
public static boolean deleteTable(String tablename){
try (HBaseAdmin admin= (HBaseAdmin)HBaseConn.getHBaseConn().getAdmin()){
admin.disableTable(tablename);
admin.deleteTable(tablename);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
/*
* @description:
* @date: 2019/07/17
* @param: [tableName, rowkye, cfName, qualifire, data]
* 表名 唯一标识 列族名 列标识 数据
* @return: boolean
*/
public static boolean putRow(String tableName,
String rowKey,
String cfName,
String qualifier,
String data){
try (Table table = HBaseConn.getTable(tableName)){
Put put = new Put(Bytes.toBytes(rowKey));
put.addColumn(Bytes.toBytes(cfName),
Bytes.toBytes(qualifier),
Bytes.toBytes(data));
table.put(put);
}catch (IOException e){
e.printStackTrace();
}
return true;
}
// 批量插入
public static boolean putRows(String tableName, List<Put> rows){
try (Table table = HBaseConn.getTable(tableName)){
table.put(rows);
}catch (IOException e){
e.printStackTrace();
}
return true;
}
// 读取一行数据
public static Result getRow(String tableName, String rowKey){
try (Table table = HBaseConn.getTable(tableName)){
Get get = new Get(Bytes.toBytes(rowKey));
return table.get(get);
}catch (IOException e){
e.printStackTrace();
}
return null;
}
// 通过过滤器获取数据
public static Result getRow(String tableName,
String rowKey,
FilterList filterList){
try (Table table = HBaseConn.getTable(tableName)){
Get get = new Get(Bytes.toBytes(rowKey));
get.setFilter(filterList);
return table.get(get);
}catch (IOException e){
e.printStackTrace();
}
return null;
}
// 使用scan扫描
public static ResultScanner getScanner(String tableName){
try (Table table = HBaseConn.getTable(tableName)){
Scan scan = new Scan();
scan.setCaching(1000);
return table.getScanner(scan);
}catch (IOException e){
e.printStackTrace();
}
return null;
}
// 全表扫描 指定开始结束 rowKey
// 只包含rowkey1 不包含rowkey2
public static ResultScanner getScanner(String tableName,
String startRowKey,
String endRowKey){
try (Table table = HBaseConn.getTable(tableName)){
Scan scan = new Scan();
scan.setStartRow(Bytes.toBytes(startRowKey));
scan.setStopRow(Bytes.toBytes(endRowKey));
scan.setCaching(1000);
return table.getScanner(scan);
}catch (IOException e){
e.printStackTrace();
}
return null;
}
// 全表扫描 指定开始结束 rowKey
// 指定过滤器
public static ResultScanner getScanner(String tableName,
String startRowKey,
String endRowKey,
FilterList filterList){
try (Table table = HBaseConn.getTable(tableName)){
Scan scan = new Scan();
scan.setFilter(filterList);
scan.setStartRow(Bytes.toBytes(startRowKey));
scan.setStopRow(Bytes.toBytes(endRowKey));
scan.setCaching(1000);
return table.getScanner(scan);
}catch (IOException e){
e.printStackTrace();
}
return null;
}
// 删除某一行
public static boolean deleteRow(String tableName, String rowKey){
try (Table table = HBaseConn.getTable(tableName)){
Delete delete = new Delete(Bytes.toBytes(rowKey));
table.delete(delete);
}catch (IOException e){
e.printStackTrace();
}
return true;
}
// 删除列族
public static boolean deleteCloumn(String tableName, String cfName){
try (HBaseAdmin admin= (HBaseAdmin)HBaseConn.getHBaseConn().getAdmin()) {
admin.deleteColumn(tableName,cfName);
}catch (Exception e){
e.printStackTrace();
}
return true;
}
// 删除列族中的qualifier
public static boolean deleteQulifier(String tableName,
String rowKey,
String cfName,
String qualifierName){
try (Table table = HBaseConn.getTable(tableName)){
Delete delete = new Delete(Bytes.toBytes(rowKey));
delete.addColumn(Bytes.toBytes(cfName),
Bytes.toBytes(qualifierName));
table.delete(delete);
table.delete(delete);
}catch (IOException e){
e.printStackTrace();
}
return true;
}
}
测试
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.client.ResultScanner;
import org.apache.hadoop.hbase.filter.*;
import org.apache.hadoop.hbase.util.Bytes;
import org.junit.Test;
import java.util.Arrays;
public class HBaseUtilTest {
@Test
public void createTable(){
HBaseUtil.
createTable("FileTable",new String[]{"fileInfo","saveInfo"});
}
@Test
public void addFileDetails(){
HBaseUtil.putRow("FileTable","rowkey3",
"fileInfo","name","file11.txt");
HBaseUtil.putRow("FileTable","rowkey3",
"fileInfo","type","txt");
HBaseUtil.putRow("FileTable","rowkey3",
"fileInfo","size","1024");
HBaseUtil.putRow("FileTable","rowkey3",
"fileInfo","creator","ergou");
HBaseUtil.putRow("FileTable","rowkey2",
"fileInfo","name","file22.txt");
HBaseUtil.putRow("FileTable","rowkey2",
"fileInfo","type","txt");
HBaseUtil.putRow("FileTable","rowkey2",
"fileInfo","size","2048");
HBaseUtil.putRow("FileTable","rowkey2",
"fileInfo","creator","dagou");
}
@Test
public void getFileDetail(){
Result result = HBaseUtil.getRow("FileTable",
"rowkey2");
if (result != null){
System.out.println("rowKey = " + Bytes.toString(result.getRow()));
System.out.println("fileName = " + Bytes.toString(
result.getValue(Bytes.toBytes("fileInfo"),
Bytes.toBytes("name"))
));
}
}
}