Hbase
WHTA
Hadoop Database
HBase
是Apache的 Hadoop
项目的子项目。
HBase
是一个高可靠性、高性能、面向列、可伸缩的分布式存储系统。
HBase
不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库, HBase
基于列的而不是基于行的模式.
HBase
的原型是Google的BigTable
论文。
WHY
MySQL
无法满足海量的数据存储,
MySQL
-> 面向行存储
HBase
-> 面向行存储
HOW
HBase概念视图
表名:webtable
两行:com.cnn.www
和 com.example.www
三个列族:contents
、anchor
、people
对于第一行com.cnn.www
anchor
列族包含两列:anchor:cssnsi.com
和 anchor:my.look.ca
{
"com.cnn.www": {
contents: {
t6: contents:html: "..."
t5: contents:html: "..."
t3: contents:html: "..."
}
anchor: {
t9: anchor:cnnsi.com = "CNN"
t8: anchor:my.look.ca = "CNN.com"
}
people: {}
}
"com.example.www": {
contents: {
t5: contents:html: "..."
}
anchor: {}
people: {
t5: people:author: "John Doe"
}
}
}
HBase物理视图
HBase
概念视图中显示的空单元根本不存储。
HBase表、行与列族
表
创建一个HBase
表:
create ‘
’,’行
HBase
中的行是逻辑上的行,物理模型上行时按列族(column family
)分别存取的。
列族
Hbase
表中的每个列都归属于某个列族,
列名由其 列族前缀 和 限定符 组成,
列的限定符是列族中数据的索引。
eg
:
列族:course
列名:course:math
,course:english
…
cell
由行和列的坐标交叉决定;
单元格是有版本的;
单元格的内容是未解析的字节数组
单元格是由行、列族、列限定符、值和代表值版本的时间戳组成的,
({row key,column( =+ ),version})
唯一确定单元格。cell中的数据是没有类型的,全部是字节码形式存储。
Name Space
命名空间,类似于关系型数据库的database概念,每个命名空间下有多个表。
HBase
有两个自带的命名空间,分别是hbase
和 default
hbase
中存放的是HBase
内置的表;default
表是用户默认使用的命名空间。
Row Key
HBase
表中的每行数据都是由一个RowKey
和多个Column
组成,数据是按照RowKey
的字典顺序存储的,并且
查询数据时只能根据RowKey
进行检索,所以RowKey
的设计十分重要。
HBase基本架构
架构角色
Region Server
Region Server 是 Region的管理者,其实现类为
HRegionServer
,主要作用:
- 对数据的操作:
get
、put
、delete
- 对Region的操作:
splitRegion
、compactRegion
- 对数据的操作:
master
Master是所有Region Server的管理者,其实现类为
HMaster
,主要作用:
- 对表的操作:create、delete、alter(变更、改变)
- 对Region Server的操作:分配regions到每个Region Server,监控每个Region Server的状态,负载均衡和故障转移
- Zookeeper
HBase
通过Zookeeper
来做master
的高可用、RegionServer
的监控、元数据的入口以及集群配置的维护等工作。 - HDFS
HDFS
为HBase
提供最终的底层数据存储服务,同时为HBase
提供高可用的支持。
RegionServer 架构
StoreFile
:保存实际数据的物理文件,StoreFile
以HFile
的形式存储在HDFS
上。每个Store
会有一个或多个StoreFile
(HFile
),数据在每个StoreFile
中都是有序的。MemStore
:写缓存,由于HFile
中的数据要求是有序的,所以数据是先存储在Memstore
中,排好序后,等到刷写时机才会刷写到HFile
,每次刷写都会形成一个新的HFile
。WAL
:由于数据要经MemStore
排序后才能刷写到HFile
,但把数据保存在内存中会后很高的概率导致数据丢失,为了解决这个问题,数据会先写在一个叫做Write-Ahead logfile
的文件中,然后再写入MemStore
中。所以在系统出现故障的时候,数据可以通过这个日志文件重建。BlockCache
:读缓存,每次查询出的数据会缓存在BlockCache
中,方便下次查询。
读写流程
将合并后的最终结果返回给客户端(此处所有数据是指同一条数据的不同版本(time stamp)或者不同的类型(Put/Delete))
HBase操作
进入hbase客户端命令行
$ bin/hbase shell
查看帮助命令
help
NameSpace相关操作
查看当前HBase中有哪些namespace
list_namespace
default # 创建表时未指定命名空间,默认在default下
hbase # 系统使用的,用来存放系统相关的元数据信息等
创建namespace
creat_namespace 'test'
creat_namespace 'test1',{'author' => 'damin','creat_time' => '2020-11-27 09:20:56'}
查看namespace
describe_namespace 'test1'
修改namespace的信息(添加或修改属性)
alter_namespace 'test1',{METHOD => 'set','author' => 'zzm'}
alter_namespace 'test',{METHOD => 'set','PROPERTY_NAME' => 'PROPERTY_VALUE'}
删除属性
alter_namespace 'test',{METHOD => 'unset',NAME => 'PROPERTY_NAME'}
删除namespace
*要注意删除的namespace必须是空的,其下没有表
drop_namespace 'test'
表的操作
查看当前数据库中有哪些表
list
创建表
creat 'student','info'
向表中插入数据
put 'student','1001','info:name','damin'
put 'student','1001','info:sex','female'
put 'student','1001','info:age','18'
扫描查看表数据
scan 'student'
scan 'student',{STARTROW => '1001',STOPROW => '1001'}
scan 'student',{STARTROW => '1001'}
查看表结构
desctibe 'student'
更新指定字段的数据
put 'student','1001','info:name','damin01'
put 'student','1001','info:age','20'
查看指定 行 或 列族:列 的数据
get 'student','1001'
get 'student','1001','info:name'
统计表数据 行数
count 'student'
删除某 rowkey 的全部数据
deleteall 'student','1001'
删除某 rowkey 的某一列数据
delete 'student','1001','info:age'
清空表数据
此操作之前,应先disable,禁用表
disable 'student'
truncate 'student'
删除表
disable 'student'
drop 'student'
变更表信息
alter 'student',{NAME => 'info',VERSIONS => 3}
get 'student','1001',{COLUMN => 'info:name',VERSIONS => 3}
DDL API
判断表是否存在
public static boolean isTableExist(String tableName) throws IOException {
//创建配置信息并配置
Configuration configuration = HBaseConfiguration.create();
configuration.set("","");
//获取与HBase的链接
Connection connection = ConnectionFactory.createConnection();
//获取DDL操作对象
Admin admin = connection.getAdmin();
//判断表是否存在
boolean exists = admin.tableExists(TableName.valueOf(tableName));
//关闭连接
admin.close();
connection.close();
//返回结果
return exists;
}
创建表
public static void createTable(String tableName,String... columnFamily) throws IOException {
//判断是否存在列族信息
if (columnFamily.length <= 0) {
System.out.println("请先设置列族信息!");
return;
}
//判断表是否存在
if (isTableExist(tableName)) {
System.out.println("表已存在!");
return;
}
//获取链接
//……
//获取DDL操作对象
Admin admin = connection.getAdmin();
//创建表描述器
HTableDescriptor tableDescriptor = new HTableDescriptor(TableName.valueOf(tableName));
//循环添加列族信息
for (String cf : columnFamily) {
//setCompressionType:设置压缩算法
tableDescriptor.addFamily(new HColumnDescriptor(cf).setCompressionType(Compression.Algorithm.NONE));
}
//执行创建表操作
admin.createTable(tableDescriptor);
}
删除表
public static void dropTable(String tableName) throws IOException {
//判断表是否存在
if (!isTableExist(tableName)) {
System.out.println("该表不存在!");
return;
}
//获取链接
//……
//获取DDL操作对象
Admin admin = connection.getAdmin();
//使表下线
admin.disableTable(TableName.valueOf(tableName));
//删除表
admin.deleteTable(TableName.valueOf(tableName));
}
DML API
插入数据 PUT
public static void putData(String tableName,String rowKey,String columnFamily,String qualifier,String value) throws IOException {
//获取链接
//……
//获取表的链接
Table table = connection.getTable(TableName.valueOf(tableName));
//创建Put对象
Put put = new Put(Bytes.toBytes(rowKey));
//放入数据
put.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(qualifier),Bytes.toBytes(value));
//向表中插入数据
table.put(put);
}
查询单条数据 GET
public static void getDate(String tableName,String rowKey,String columnFamily,String qualifier) throws IOException {
//获取链接
//……
//获取表的链接
Table table = connection.getTable(TableName.valueOf(tableName));
//创建Get对象
Get get = new Get(Bytes.toBytes(rowKey));
//指定列族查询
get.addFamily(Bytes.toBytes(columnFamily));
//指定列族:列查询
get.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(qualifier));
//查询数据
Result result = table.get(get);
//解析result
for (Cell cell : result.rawCells()) {
System.out.println("ColumnFamily:" + Bytes.toString(cell.getFamilyArray()) +
",Qualifier:" + Bytes.toString(cell.getQualifierArray()) +
",Value:" + Bytes.toString(cell.getValueArray()));
}
}
扫描数据 SCAN
public static void scanTable(String tableName) throws IOException {
//获取链接
//……
//获取表的链接
Table table = connection.getTable(TableName.valueOf(tableName));
//扫描数据
ResultScanner results = table.getScanner(new Scan());
//解析数据
for (Result result : results) {
for (Cell cell : result.rawCells()) {
System.out.println(Bytes.toString(CellUtil.cloneRow(cell)) + ":" +
Bytes.toString(CellUtil.cloneFamily(cell)) + ":" +
Bytes.toString(CellUtil.cloneQualifier(cell)) + ":" +
Bytes.toString(CellUtil.cloneValue(cell)));
}
}
}
删除数据 DELETE
public static void deleteData(String tableName,String rowKey,String columnFamily,String qualifier) throws IOException {
//获取链接
//……
//获取表的链接
Table table = connection.getTable(TableName.valueOf(tableName));
//创建Delete对象
Delete delete = new Delete(Bytes.toBytes(rowKey));
//指定列族删除数据
delete.addFamily(Bytes.toBytes(columnFamily));
//指定 列族:列 删除数据 (没有指定时间戳,删除最新版本)
delete.addColumn(Bytes.toBytes(columnFamily),Bytes.toBytes(qualifier));
//删除<=指定时间戳的所有版本
delete.addColumns(Bytes.toBytes(columnFamily),Bytes.toBytes(qualifier));
//执行删除操作
table.delete(delete);
}