这一篇博文是【大数据技术●降龙十八掌】系列文章的其中一篇,点击查看目录:大数据技术●降龙十八掌
系列文章:
:【十八掌●武功篇】第八掌:HBase之基本概念
【十八掌●武功篇】第八掌:HBase之Shell
【十八掌●武功篇】第八掌:HBase之基本操作Java API
【十八掌●武功篇】第八掌:HBase之过滤器总结
【十八掌●武功篇】第八掌:HBase之性能调优
【十八掌●武功篇】第八掌:HBase之安装与集成 [草稿]
HBase Java API使用Put对象封装一行数据,包括rowkey、列族信息、列标签信息、单元格版本信息、单元格值。然后使用Put对象对Table中的数据进行写入,包括插入和更新操作。
Put对象插入和更新HBase数据适合小数据量的写操作。
private static void test1() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
//实例化一个Put对象,指定Rowkey
Put put = new Put(Bytes.toBytes("100003_00201612_500000"));
//向Put对象里添加一列,列族为cf,列标签为factory_id,值为100003,版本默认为当前时间戳
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"),Bytes.toBytes("100003"));
//向Put对象里添加一列,列族为cf,列标签为log_date,值为00201612
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("log_date"),Bytes.toBytes("00201612"));
//将put对象添加进table
table.put(put);
} catch (IOException e) {
e.printStackTrace();
}
finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Table表的put方法可以传入一个List。
当列表中的多个Put有部分出错时,不会影响其他Put的写入操作。
private static void test3() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
List list = new ArrayList<>();
//实例化一个Put对象,指定Rowkey
Put put = new Put(Bytes.toBytes("100003_00201612_500000"));
//向Put对象里添加一列,列族为cf,列标签为factory_id,值为100003,版本默认为当前时间戳
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"), Bytes.toBytes("100008"));
list.add(put);
Put put2 = new Put(Bytes.toBytes("100004_00201612_500000"));
//向Put对象里添加一列,列族为cf,列标签为factory_id,值为100003,版本默认为当前时间戳
put2.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"), Bytes.toBytes("100009"));
list.add(put2);
table.put(list);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
每一个table的put操作都是一个客户端和HBase服务器的RPC通信操作,高频率的写入操作时会产生大量的网络数据传输次数,效率比较低。
解决这个问题的一种方法是使用BufferedMutator对象,使用缓冲区,缓冲区负责收集put操作,然后调用RPC操作一次性地put到HBase服务器上。当缓冲区里的数据量到达一定的阀值时或者在调用BufferedMutator的close()方法时,客户端能自动提交更新到HBase服务器。
private static void test2() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
BufferedMutator t = null;
try {
connection = ConnectionFactory.createConnection(conf);
t = connection.getBufferedMutator(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
Put put = null;
//实例化一个Put对象,指定Rowkey,将put对象添加进table
put = new Put(Bytes.toBytes("100004_00201612_500000"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"), Bytes.toBytes("100004"));
t.mutate(put);
//实例化一个Put对象,指定Rowkey,将put对象添加进table
put = new Put(Bytes.toBytes("100005_00201612_500000"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"), Bytes.toBytes("100005"));
t.mutate(put);
//实例化一个Put对象,指定Rowkey,将put对象添加进table
put = new Put(Bytes.toBytes("100006_00201612_500000"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"), Bytes.toBytes("100006"));
t.mutate(put);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (t != null) {
t.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
原子性Put可以保证Put操作本身是原子性的,它是通过在更新之前先检查现有的值,如果和预期一致则进行更新操作,如果不一致就放弃Put操作。
这种原子性的Put非常适用于多客户端并发修改的时候使用。
private static void test4() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
Put put = new Put(Bytes.toBytes("100003_00201612_500000"));
put.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"), Bytes.toBytes("100004"));
//第一个参数为Rowkey,第二个参数为列族,第三个参数为列标签,第四个参数为参考值,第五个为put对象
boolean isPass = table.checkAndPut(Bytes.toBytes("100003_00201612_500000"), Bytes.toBytes("cf"),
Bytes.toBytes("factory_id"), Bytes.toBytes("100006"), put);
if (isPass) {
System.out.println("更新成功!");
} else {
System.out.println("放弃操作!");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
可以使用Get对象来读取HBase里的一行数据,可以指定读取的列族、列标签、版本。
private static void test1() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
//实例化一个Get对象
Get get = new Get(Bytes.toBytes("100003_00201612_500000"));
//添加一个要Get的列族
get.addFamily(Bytes.toBytes("cf"));
//添加一个要Get的列标签
get.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
//添加一个要Get的列标签
get.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("log_date"));
//去HBase服务器执行查询
Result result = table.get(get);
System.out.println(result);
//结果行的RowKey
String rowKey=Bytes.toString(result.getRow());
//打印结果
for (Cell cell:result.listCells()){
//列名
String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
//单元格的值
String val = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println("RowKey:" + rowKey + "列:" + qualifier + ";值长度:" + CellUtil.cloneValue(cell).length + ";值:" + val);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
如上个Get代码实例中所示:使用Get读取数据返回的数据被封装在一个Result对象里,可以通过getRow()方法来获取RowKey,Result对象的listCells属性是查询结果中所有的单元格,可以借助于CellUtil类来获取每个单元格的列标签信息、单元格值信息。
类似于Put列表,Get列表是可以一次请求多行数据的。
private static void test2() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
List list = new ArrayList<>();
//实例化一个Get对象
Get get = new Get(Bytes.toBytes("100003_00201612_500000"));
//添加一个要Get的列标签
get.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
list.add(get);
//实例化一个Get对象
Get get1 = new Get(Bytes.toBytes("100004_00201612_500000"));
//添加一个要Get的列标签
get1.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
list.add(get1);
//实例化一个Get对象
Get get2 = new Get(Bytes.toBytes("100005_00201612_500000"));
//添加一个要Get的列标签
get2.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
list.add(get2);
//去HBase服务器执行查询
Result[] results = table.get(list);
//打印结果
for (Result result : results) {
//结果行的RowKey
String rowKey = Bytes.toString(result.getRow());
for (Cell cell : result.listCells()) {
//列名
String qualifier = Bytes.toString(CellUtil.cloneQualifier(cell));
//单元格的值
String val = Bytes.toString(CellUtil.cloneValue(cell));
System.out.println("RowKey:" + rowKey + "列:" + qualifier + ";值长度:" + CellUtil.cloneValue(cell).length + ";值:" + val);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void test1(){
Configuration conf=HBaseConfiguration.create();
Connection connection=null;
Table table=null;
try {
connection= ConnectionFactory.createConnection(conf);
table=connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
//创建一个Delete对象,指定Rowkey
Delete delete=new Delete(Bytes.toBytes("100003_00201612_500000"));
//指定删除某一个列,只删除最新版本
delete.addColumn(Bytes.toBytes("cf"),Bytes.toBytes("factory_id"));
//指定删除某一个列族
delete.addFamily(Bytes.toBytes("cf"));
//指定删除方法
table.delete(delete);
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private static void test2() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
List list = new ArrayList<>();
//创建一个Delete对象,指定Rowkey
Delete delete = new Delete(Bytes.toBytes("100004_00201612_500000"));
//指定删除某一个列,只删除最新版本
delete.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
list.add(delete);
Delete delete2 = new Delete(Bytes.toBytes("100006_00201612_500000"));
//指定删除某一个列,删除所有版本
delete2.addColumns(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
list.add(delete2);
//删除所有的列族、列、所有版本
Delete delete3 = new Delete(Bytes.toBytes("100005_00201612_500000"));
list.add(delete3);
//指定删除方法
table.delete(list);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (table != null) {
table.close();
}
if (connection != null) {
connection.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
HBase中的数据是按照RowKey顺序存储的,扫描表是根据RowKey进行扫描,可以指定扫描的开始rowkey和结束rowkey,但是扫描结果里,是包括开始rowkey的,但是并不包括结束rowkey,如果没有指定开始rowkey,就从表的开始位置扫描,如果没有指定结束rowkey,就扫描到表的末尾。
另外可以指定扫描哪个列族、哪个列、开始Rowkey、结束RowKey、过滤器等。
private static void test1() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
Table table = null;
try {
connection = ConnectionFactory.createConnection(conf);
table = connection.getTable(TableName.valueOf("DLR:ft_fact_data_month_quarter"));
Scan scan = new Scan();
//指定开始rowkey
scan.setStartRow(Bytes.toBytes("000003_00020164_000000"));
//指定结束rowkey
scan.setStopRow(Bytes.toBytes("000004_20161117_340000"));
//指定扫描哪个列族
scan.addFamily(Bytes.toBytes("cf"));
//指定扫描哪个列
scan.addColumn(Bytes.toBytes("cf"), Bytes.toBytes("factory_id"));
//得到一个表扫描器
ResultScanner resultScanner = table.getScanner(scan);
//循环打印扫描结果
for (Result result : resultScanner) {
//打印rowkey
System.out.println("RowKey:" + result.getRow());
String columnsVal="";
//循环一行中的各个单元格
for(Cell cell:result.listCells()){
//获取单元格所在的列族
String family= Bytes.toString(CellUtil.cloneFamily(cell));
//获取单元格所在的列
String qualifier= Bytes.toString(CellUtil.cloneQualifier(cell));
//获取单元格所在的值
String val=Bytes.toString(CellUtil.cloneValue(cell));
columnsVal+="列族:"+family+",列:"+qualifier+",值:"+val+";";
}
System.out.println(columnsVal);
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用HTableDescriptor对象进行对表的模式定义,实例如下:
private void test1()
{
HTableDescriptor tableDesc=new HTableDescriptor(TableName.valueOf("Test:demo1"));
HColumnDescriptor columnDesc=new HColumnDescriptor("cf");
//添加列族
tableDesc.addFamily(columnDesc);
//设置region分割点大小
tableDesc.setMaxFileSize(524288000);
//设置表只读
tableDesc.setReadOnly(true);
//设置刷写大小
tableDesc.setMemStoreFlushSize(33554432);
//设置用户自定义参数
tableDesc.setValue("mykey","myval");
//获取自定义参数
tableDesc.getValue("mykey");
//移出自定义参数
tableDesc.remove("mykey");
}
(1) 列族
HBase建表时要指定列族,之后可以添加列族、判断列族是否存在、获取列族列表、获取某一个列族的描述、删除列族等操作。使用HColumnDescriptor对象来对列族进行定义,后面会详细说明HColumnDescriptor。
(2) 文件大小限制
当一个region大小达到配置的大小的时候,HBase会拆分region,这个region进行拆分的分割点可以配置,HTableDescriptor对象的setMaxFileSize()方法来进行设置,当表的数据量特别大时候,可以适量地调高这个值。
(3) 表只读
默认的表是可写的,如果将一个表设置为只读,可以调用HTableDescriptor对象的setReadOnly方法来设置。
(4) 写缓冲区大小
HBase的内存中会预留一个写缓冲区,写入的数据会先存放在写缓冲区中,然后再存入文件中,可以用HTableDescriptor的setMemStoreFlushSize方法设置缓冲区的大小。
(5) 自定义参数
用户可以自定义一些键值对到HTable上。
列族的模式定义使用HColumnDescriptor类来封装
(1) 列族名称
每个列族都用名字,但是除了构造函数,没有其他重命名列族的途径。
(2) 最大版本数
列族中限制了每个值能保留的最大版本数量。
(3) 压缩
HBase支持插件式的压缩算法,可以用的压缩算法如下:
算法 | 描述 |
---|---|
NONE | 不压缩(HBase默认值) |
GZ | 使用Java提供的或者本地库提供的GZIP |
LZO | 需要独立安装LZO的类库 |
SNAPPY | 需要独立安装 |
通过HColumnDescriptor的setCompressionType来设置压缩算法,压缩类型是Compression.Algorithm枚举值。
(4) 块大小
HBase中存储的文件会被划分为很多小块,这些小块在get或者scan的时候会被加载到内存中,可以设置这个块的大小。
这个块不同于HDFS中的分块,HDFS中分块是为了分布式存储拆分的,并利于MapReduce的计算而划分的,比较大;这里的块用于HBase高效地在内存中加载和缓冲数据,只用于HBase内部,比较小。
(5) 是否开启读取缓存块
默认是是打开的,读取数据时把数据存入缓存并不一定能提高性能,如果只是对列族的get操作和顺序化地scan操作,没有必要启用读取缓存。当有对一个rowkey要进行连续反复访问时,适合将读取缓存打开。
(6) 生存期
值不但可以指定版本数,也可以设置保存的最大生命周期,如果超过设置的时间,数据就会被删除。单位是秒,默认是Integer.MAX_VALUE。
(7) 是否在内存
当设置为true的时候,列族的数据会被优先放入读取缓存。
(8) 布隆过滤器
布隆过滤器可以提高某些时候的查询时间,但是它会增加内存和存储的开销,默认布隆过滤器是被关闭的,可以通过setBloomFilterType设置布隆过滤器,值为BloomType类型的枚举。
类型 | 描述 |
---|---|
NONE | 不使用布隆过滤器 |
ROW | 在rowkey上使用布隆过滤器进行过滤 |
ROWCOL | 在列标识符级别使用布隆过滤器 |
表操作需要创建一个HBaseAdmin实例来进行各种操作。
(1) 创建表
private void test1() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
//创建HBaseAdmin实例
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf("Test:demo1"));
HColumnDescriptor columnDesc = new HColumnDescriptor("cf");
//添加列族
tableDesc.addFamily(columnDesc);
//设置region分割点大小
tableDesc.setMaxFileSize(524288000);
//设置表只读
tableDesc.setReadOnly(true);
//设置刷写大小
tableDesc.setMemStoreFlushSize(33554432);
//设置用户自定义参数
tableDesc.setValue("mykey", "myval");
//获取自定义参数
tableDesc.getValue("mykey");
//移出自定义参数
tableDesc.remove("mykey");
//创建表
admin.createTable(tableDesc);
} catch (IOException e) {
e.printStackTrace();
}
}
(2) 预分区建表
预分区创建表时将在创建表时划分出若干特定的Region。
private static void test2() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf("weibo:demo2"));
HColumnDescriptor columnDesc = new HColumnDescriptor("cf");
tableDesc.addFamily(columnDesc);
//创建预分区表,指定rowkey边界和分区数
admin.createTable(tableDesc, Bytes.toBytes(1L),Bytes.toBytes(100L),10);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void test3() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
if (!admin.tableExists(TableName.valueOf("weibo:demo3"))) {
HTableDescriptor tableDesc = new HTableDescriptor(TableName.valueOf("weibo:demo3"));
HColumnDescriptor columnDesc = new HColumnDescriptor("cf");
tableDesc.addFamily(columnDesc);
//指定划分region的边界值
byte[][] regions = new byte[][]{
Bytes.toBytes("A"),
Bytes.toBytes("B"),
Bytes.toBytes("C"),
Bytes.toBytes("D"),
Bytes.toBytes("E")
};
//创建预分区表,指定rowkey边界和分区数
admin.createTable(tableDesc, regions);
}
} catch (IOException e) {
e.printStackTrace();
}
}
(3) 获取表结构
private static void test4() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
if (admin.tableExists(TableName.valueOf("weibo:demo3"))) {
HTableDescriptor desc = admin.getTableDescriptor(TableName.valueOf("weibo:demo3"));
System.out.println(desc);
}
} catch (IOException e) {
e.printStackTrace();
}
}
(4) 删除表
在删除表之前要先将表禁用,禁用时会现将内存中未提交到磁盘数据刷写到磁盘,然后关闭所有region,并更新表的元数据,将所有的region标记为下线。删除表后,对应的数据也会被删除。
private static void test5() {
Configuration conf = HBaseConfiguration.create();
Connection connection = null;
try {
connection = ConnectionFactory.createConnection(conf);
HBaseAdmin admin = (HBaseAdmin) connection.getAdmin();
TableName tableName = TableName.valueOf("weibo:demo3");
if (admin.tableExists(tableName)) {
HTableDescriptor desc=admin.getTableDescriptor(tableName);
System.out.println("禁用前表当前状态isTableAvailable:"+admin.isTableAvailable(tableName));
System.out.println("禁用前表isTableEnabled:"+admin.isTableEnabled(tableName));
admin.disableTable(tableName);
System.out.println("禁用后表当前状态isTableAvailable:"+admin.isTableAvailable(tableName));
System.out.println("禁用后表isTableEnabled:"+admin.isTableEnabled(tableName));
admin.deleteTable(tableName);
System.out.println("删除后表当前状态isTableAvailable:"+admin.isTableAvailable(tableName));
admin.createTable(desc);
System.out.println("创建表后表当前状态isTableAvailable:"+admin.isTableAvailable(tableName));
System.out.println("创建表后表isTableEnabled:"+admin.isTableEnabled(tableName));
}
} catch (IOException e) {
e.printStackTrace();
}
}
输出:
禁用前表当前状态isTableAvailable:true
禁用前表isTableEnabled:true
禁用后表当前状态isTableAvailable:true
禁用后表isTableEnabled:false
删除后表当前状态isTableAvailable:false
创建表后表当前状态isTableAvailable:true
创建表后表isTableEnabled:true