大数据之路系列之HBASE(06)
hbase
一、hbase是什么?
1.HBASE的特点
hbase是列储存数据库,更适合OLTP场景,能实现事务,行级别的增删、更新操作。它有以下几个特点
- 高可靠——HBASE集群支撑多个节点,集群可靠性高;数据在hdfs上,数据可靠性高。
- 高性能——HBASE在几十亿数据数据可以实现毫秒级别的查询
- 面向列——HBASE的存储是列存储方式
- 可伸缩——HBASE的节点可以方便增加或者删除
2.HBASE的增删改查
hdfs不支持增删改查,HBASE依赖的hdfs,为什么HBASE可以支持增删改查呢?
原因是HBASE自己封装了一个中间层,对于删除的数据,打上标记,查询进行了过滤。
二、行存储数据库
每一行作为一个存储单元进行存储
传统的MySQL、Oracle都是行存储数据库,使用sql进行查询。
select … from … where … 其中的where就是进行的行数过滤。
对于行数据库查询效率来说,查询的列数量影响不大;扫描的行数是决定效率的关键。即便只需要一列的数据,也是需要扫描所有行和所有列然后在对列进行过滤。
三、列存储数据库
每一列作为一个存储单元进行存储
列存储数据库对于查询某列的数据是快速的。但是,查询所有行就很慢了。
在大数据实际中更多的是查询某一列数据,或者是少数几列。所以说列存储更合适。
四、HBase典型应用场景
- 适合半结构化非结构化数据——比如新闻,标签字段不固定有可能1个有可能10个,对于传统数据库,只能是放在一个字段里面拼接,但是这样不利于取;放10个字段中,又会导致冗余。HBASE可以方便的添加字段。
- 纪录稀疏——HBASE中null值不会存储,这样这样节约空间又能提高读性能
- 多版本数据——HBASE自带历史存储数据
- 超大数据量——只需要加机器即可,HBase会自动水平切分扩展,跟Hadoop的无缝集成保障了其数据可靠性(HDFS)和海量数据分析的高性能(MapReduce)。
五、HBase常用的shell命令
1.DDL命令
命令 解释
create 创建表
list 列出所有表
disable / is_disabled 禁用表/验证表是否被禁用
enable / is_enabled 启用表/验证表是否已启用
desc 查看表的详细信息
alter 修改表结构
exists 验证表是否存在
drop / truncate 删除表/清空表(删除重建)
-
创建表命令:
create ‘表名’,‘列族1’,‘列族2’,'列族N’
注意:单引号不能少,只能使用单引号,不能使用双引号。
例子:create ‘student’,‘info’,‘level’
解释:创建了一张名为student的表,表中有两个列族,分别是info和level。create后面的第一个参数是表名,后面的都是列族的名称
注意:创建表时不能指定列,只能指定列族。 -
列出所有表
命令:list -
禁用表/验证表是否被禁用
禁用表
命令:disable ‘表名’ -
验证表是否被禁用
命令:is_disabled '表名’
解释:返回true表示此表被禁用,此时这个表不能被操作,因为表中的数据是存在Region中的,当Region中的数据达到一定量级的时候会进行分裂,产生多个Region,多个Region会分到其它节点上面。这个时候,数据是不能被操作的,所以会有disabled这个状态。 -
启用表/验证表是否已启用
启用表
命令:enable '表名’
解释:当表被手工禁用之后,想要恢复使用,需要启动表 -
禁用表/验证表是否被禁用
禁用表
命令:disable ‘表名’ -
查看表的详细信息
命令:desc ‘表名’ 或者 desc ‘表名’ -
修改表结构(修改列族)
使用alter命令可以完成更改列族参数信息、增加列族、删除列族以及更改表等操作,在这里主要演示一下增加列族、修改列族和删除列族
修改列族的参数信息,例如:修改列族的版本
通过desc命令可以看到student表中列族的版本个数:
NAME => ‘info’, VERSIONS => ‘1’
NAME => ‘level’, VERSIONS => ‘1’
alter ‘student’,{NAME=>‘level’,VERSIONS=>‘3’}
这里显示的这两个列族版本个数都是1,说明这两个列族中的所有列的值只能保存最新的1份数据,如果想要保存level列族中所有列的最近3个数据历史版本,可以这样操作:
注意:1.如果数据很大的时候,修改会很长时间。2.这个版本是和列族绑定的,不是和某一列绑定的。
-
修改表结构(增加列族)
在已存在的表的基础之上增加列族,在这我们向student表中增加一个列族:about
alter ‘student’,‘about’ -
修改表结构(删除列族)
删除表中已有的列族,在这我们删除student中的about这个列族
alter ‘student’,{NAME=>‘about’,METHOD=>‘delete’}
注意:HBase 中的表至少要包含一个列族,因此当表中只有一个列族时,无法将其删除。
-
验证表是否存在
命令:exists '表名’ exists ‘student’
解释:表存在返回true,否则返回false -
删除表
命令:drop ‘表名’ drop ‘t1’
-清空表
清空表其实包含了两步,删除+重建
命令:truncate ‘表名’
truncate ‘t2’
2.增删改查命令
命令 解释
put 添加数据/修改数据
get 查看数据
count 查看表中数据总条数
scan 扫描表中的数据
delete / deleteall 删除数据
- 添加数据/修改数据
HBase中没有insert方法,它也属于key-value类型的NoSQL数据库,类似于HashMap这种数据结构,所以它提供了put方法添加数据
命令:put ‘表名’,‘Rowkey’,‘列族:列’,'value’
添加2条数据,rowkey分别为:jack和tom
hbase(main):001:0> put 'student','jack','info:sex','man'
Took 0.8415 seconds
hbase(main):002:0> put 'student','jack','info:age','22'
Took 0.0295 seconds
hbase(main):003:0> put 'student','jack','level:class','A'
Took 0.0300 seconds
hbase(main):004:0> put 'student','tom','info:sex','woman'
Took 0.0286 seconds
hbase(main):005:0> put 'student','tom','info:age','20'
Took 0.0275 seconds
hbase(main):006:0> put 'student','tom','level:class','B'
Took 0.0228 seconds
注意:HBase中没有修改方法,重复执行put就是修改操作了。put操作在执行的时候,如果指定的数据已经存在,则更新,否则就新增。
-
查看数据
查看数据的时候有这么几种用法
命令:get ‘表名’,'Rowkey’
命令:get ‘表名’,‘Rowkey’,'列族’
命令:get ‘表名’,‘Rowkey’,‘列族:列’查询student中rowkey等于jack的所有列族中的数据
get ‘student’,‘jack’查询student中rowkey等于jack的info列族中的数据
get ‘student’,‘jack’,‘info’查询student中rowkey等于jack的info列族中的age列的数据
get ‘student’,‘jack’,‘info:age’ -
查看表中数据总条数
命令:count ‘表名’ count ‘student’ -
扫描表中的数据
命令:scan '表名’ scan ‘student’
扫描表中的所有数据 -
删除数据
delete有这么几种用法:
命令:delete ‘表名’,‘Rowkey’,'列族:列’
命令:delete ‘表名’,‘Rowkey’,‘列族:列’,时间戳删除指定Rowkey中指定列族中指定列的数据
delete ‘student’,‘jack’,‘info:age’删除指定Rowkey中指定列族中指定列中时间戳小于2的数据
delete ‘student’,‘jack’,‘info:age’,2
注意:delete 操作并不会马上删除数据,只会将对应的数据打上删除标记,只有在HBase底层合并数据时,数据才会被真正删除。
3.HBase的namespace(命名空间)
-
HBase的命名空间相当于MySQL中的Database
HBase默认有2个命名空间:分别是hbase和default
其中hbase存放系统表,default是存放用户表
使用list_namespace命令可以查看所有的命名空间,我们创建的表默认在default这个命名空间里面 -
可以选择创建一个新的namespace
create_namespace ‘n1’ -
在创建表的时候可以选择创建到n1这个namespace中,如何实现呢?
使用这种格式即可:‘命名空间名称:表名’
针对default这个命名空间,在使用的时候可以省略不写
create ‘n1:t1’,‘info’,‘level’ -
如果只想查看n1这个命名空间中的表,如何实现呢?
可以使用命令list_namespace_tables
list_namespace_tables ‘n1’
4.HBase JavaAPI的使用
<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>2.2.7</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.10</version>
</dependency>
package com.imooc.hbase;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.hbase.Cell;
import org.apache.hadoop.hbase.CellUtil;
import org.apache.hadoop.hbase.HBaseConfiguration;
import org.apache.hadoop.hbase.TableName;
import org.apache.hadoop.hbase.client.*;
import org.apache.hadoop.hbase.util.Bytes;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* 操作HBase
* 表:创建、删除
* 数据:增、删、改、查
* Created by xuwei
*/
public class HBaseOp {
public static void main(String[] args) throws Exception {
//获取HBase数据库连接
Connection conn = getConn();
//添加数据
//put(conn);
//查询数据
//get(conn);
/**
* 查询多版本的数据
* 当列的值有多个历史版本的时候
*
* 修改列族info的最大历史版本存储数量
* alter 'student',{NAME=>'info',VERSIONS=>3}
*
* 然后再执行下面命令,向列族info中的age列中添加几次数据,实现多历史版本数据存储
* put 'student','laowang','info:age','19'
* put 'student','laowang','info:age','20'
*
*/
//getMoreVersion(conn);
//修改数据--同添加数据
//删除数据
//delete(conn);
//==============================分割线======================
//获取管理权限,负责对HBase中的表进行操作(DDL操作)
Admin admin = conn.getAdmin();
//创建表
//createTable(admin);
//删除表
//deleteTable(admin);
//关闭admin连接
admin.close();
//关闭连接
conn.close();
}
/**
* 删除表
* @param admin
* @throws IOException
*/
private static void deleteTable(Admin admin) throws IOException {
//删除表,先禁用表
admin.disableTable(TableName.valueOf("test"));
admin.deleteTable(TableName.valueOf("test"));
}
/**
* 创建表
* @param admin
* @throws IOException
*/
private static void createTable(Admin admin) throws IOException {
//指定列族信息
ColumnFamilyDescriptor familyDesc1 = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("info"))
//在这里可以给列族设置一些属性
.setMaxVersions(3)//指定最多存储多少个历史版本数据
.build();
ColumnFamilyDescriptor familyDesc2 = ColumnFamilyDescriptorBuilder.newBuilder(Bytes.toBytes("level"))
//在这里可以给列族设置一些属性
.setMaxVersions(2)//指定最多存储多少个历史版本数据
.build();
ArrayList<ColumnFamilyDescriptor> familyList = new ArrayList<ColumnFamilyDescriptor>();
familyList.add(familyDesc1);
familyList.add(familyDesc2);
//获取TableDescriptor对象
TableDescriptor desc = TableDescriptorBuilder.newBuilder(TableName.valueOf("test"))//指定表名
.setColumnFamilies(familyList)//指定列族
.build();
//创建表
admin.createTable(desc);
}
/**
* 删除数据
* @param conn
* @throws IOException
*/
private static void delete(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Delete对象
Delete delete = new Delete(Bytes.toBytes("laowang"));
//【可选】可以在这里指定要删除指定Rowkey数据哪些列族中的列
//delete.addColumn(Bytes.toBytes("info"),Bytes.toBytes("age"));
table.delete(delete);
//关闭table连接
table.close();
}
/**
* 查询多版本的数据
* @param conn
* @throws IOException
*/
private static void getMoreVersion(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Get对象
Get get = new Get(Bytes.toBytes("laowang"));
//读取cell中的所有历史版本数据,不设置此配置的时候默认只读取最新版本的数据
//可以通过get.readVersions(2)来指定获取多少个历史版本的数据
get.readAllVersions();
Result result = table.get(get);
//获取指定列族中指定列的所有历史版本数据,前提是要设置get.readAllVersions()或者get.readVersions(2),否则只会获取最新数据
List<Cell> columnCells = result.getColumnCells(Bytes.toBytes("info"), Bytes.toBytes("age"));
for (Cell cell: columnCells) {
byte[] value_bytes = CellUtil.cloneValue(cell);
long timestamp = cell.getTimestamp();
System.out.println("值为:"+new String(value_bytes)+",时间戳:"+timestamp);
}
//关闭table连接
table.close();
}
/**
* 查询数据
* @param conn
* @throws IOException
*/
private static void get(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回Get对象
Get get = new Get(Bytes.toBytes("laowang"));
//【可选】可以在这里指定要查询指定Rowkey数据哪些列族中的列
//如果不指定,默认查询指定Rowkey所有列的内容
get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"));
get.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"));
Result result = table.get(get);
//如果不清楚HBase中到底有哪些列族和列,可以使用listCells()获取所有cell(单元格),cell对应的是某一列的数据
List<Cell> cells = result.listCells();
for (Cell cell : cells) {
//注意:下面获取的信息都是字节类型的,可以通过new String(bytes)转为字符串
//列族
byte[] famaily_bytes = CellUtil.cloneFamily(cell);
//列
byte[] column_bytes = CellUtil.cloneQualifier(cell);
//值
byte[] value_bytes = CellUtil.cloneValue(cell);
System.out.println("列族:"+new String(famaily_bytes)+",列:"+new String(column_bytes)+",值:"+new String(value_bytes));
}
System.out.println("==============================================");
//如果明确知道HBase中有哪些列族和列,可以使用getValue(family,qualifier)直接获取指定列族中指定列的数据
byte[] age_bytes = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
System.out.println("age列的值:"+new String(age_bytes));
//关闭table连接
table.close();
}
/**
* 添加数据
*
* @param conn
* @throws IOException
*/
private static void put(Connection conn) throws IOException {
//获取Table,指定要操作的表名,表需要提前创建好
Table table = conn.getTable(TableName.valueOf("student"));
//指定Rowkey,返回put对象
Put put = new Put(Bytes.toBytes("laowang"));
//向put对象中指定列族、列、值
//put 'student','laowang','info:age','18'
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("18"));
//put 'student','laowang','info:sex','man'
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("sex"), Bytes.toBytes("man"));
//put 'student','laowang','level:class','A'
put.addColumn(Bytes.toBytes("level"), Bytes.toBytes("class"), Bytes.toBytes("A"));
//向表中添加数据
table.put(put);
//关闭table连接
table.close();
}
/**
* 获取连接
*
* @return
* @throws IOException
*/
private static Connection getConn() throws IOException {
//获取配置
Configuration conf = HBaseConfiguration.create();
//指定HBase使用的zk的地址,多个用逗号隔开
conf.set("hbase.zookeeper.quorum", "bigdata01:2181,bigdata02:2181,bigdata03:2181");
//指定HBase在hdfs上的根目录
conf.set("hbase.rootdir", "hdfs://bigdata01:9000/hbase");
//创建HBase连接,负责对HBase中的数据的一些增删改查(DML操作)
return ConnectionFactory.createConnection(conf);
}
}
总结
TODO