HBase编程示例

通过HBase的api进行java编程来操作HBase数据库.

HBase是运行在hadoop之上的NoSQL数据库

先复习下之前的吧

理论温习

HBase的优势:

  1. HBase擅长解决大数据随机访问问题, 随机读和随机写
  2. HBase集群能力容易扩展, 理想的处理大数据存储和计算
  3. HBase数据表的列可以根据需要动态定义的.

HBase的应用场景

  1. 内存缓存应用-通过增加节点增加内存容量
  2. 大容量数据集, 但某些少量子集数据经常被访问.
  3. 通过在内存中缓存数据, 减少磁盘I/O访问, 增加数据查询速度
  4. HBase擅长通过row key进行检索.
1
2
3
4
使用HBase:
1. 当需要随机写入或者随机读取, 或者两种情况都有
2. 你的应用程序在几个TB的数据集中需要实现每秒几千次的读写操作
3. 访问模式明确, 数据关系比较简单. 通常都是单表.

HBase的劣势

  1. 不能提供RDBMS(relation database management system)具有的典型特征
  2. 不能集成sql支持, 可以通过外部工具(Hive等提供sql访问)
  3. 不支持事务
  4. 不支持多索引
  5. 不能遵守ACID原则,

HBase术语

  1. Node(节点)
    • 集群中单独的一台机器
  2. Cluster(集群)
    • 一组互相连接的机器, 由中央节点统一协调来执行任务
  3. Master Node(主节点)
    • 执行协调任务的节点
  4. Worker Node(从节点)
    • 执行由主节点分配的任务
  5. Daemon(进程)
    • 后台运行的程序进程.

HBase table

  1. hbase table由rows, columns, column families组成
  2. 每一行都有一个唯一的row key, 可以用作快速查找
  3. column存储数据
  4. 每一个column都属于一个指定的column family(列族)
  5. 每个表有一个或多个 column family
1
2
3
1. hbase本质上是一个分布式的map存储
2. distributed: hbase架构上设计成运行在多台机器上存储和服务表中的数据
3. Map: hbase以map形式存储数据, 并且保证相邻rowkey的数据存储在磁盘上相邻的位置.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
1. 属于同一column family(列族)下的column有着同样的前缀.
: info:name和info:age
通过冒号分割列族和列
2. 一个列族可以包含任意多的列
一个列族内的列都存储在一起.
3. 一个column通过column family和column名字引用
4. 例子:

column families .... contactInfo privateInfo
column names RowKey Fname Lname Image
rows rk001 Dae Jong hello.jpg
rk002 Dae1 Jong1 hello.jpg
rk001 Dae2 Jong1 hello.jpg
rk001 Dae3 Jong1 hello.jpg


5. 数据在hbase中以字节数组存储. 这个在java 编程时 要使用Byte的方法进行转化.
6. 数据在磁盘中存储, 会按照每个column family方式存储在磁盘上.
7. 空的cell不会占用存储空间
8. 例子:
contactInfo
RowKey Fname Lname
rk001 Dae Jong
rk002 Dae1 Jong1
rk003 Dae2 Jong2

privateInfo
RowKey image
rk001 hello.jpg
rk002 hello.jpg
rk003 hello.jpg

9. column family的数据存储
rowkey + column + tmestamp -> value
rowkey和value都是字节

RowKey Column Timestamp Cell Value
rk001 contactInfo:Fname 12301982391 Dae
rk001 contactInfo:Lname 12332982391 Jong
rk002 contactInfo:Fname 12331282391 Dae1
rk002 contactInfo:Lname 12376982391 Jong1
rk001 privateInfo:image 12301982234 Hello.jpg

数据会按照Row key和Column的顺序排序.

10. 操作hbase table
增删改查:(put, get/scan, delete)
在hbase中rowkey是唯一的索引. 不同于RDBMS可以有多个索引.
hbase table不支持表连接查询数据.

代码实战.

老规矩: maven构建项目

pom.xml文件添加依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
1. 方便起见 添加最少的依赖, 让这些依赖自动去下载他们所需要的其他依赖.

<dependencies>

<!-- 该依赖同时依赖好多其他的jar, hdfs, common, yarn, mapreduce等等-->
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
<version>2.7.3</version>
</dependency>

<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-client</artifactId>
<version>1.1.2</version>
</dependency>

<dependency>
<groupId>org.apache.hbase</groupId>
<artifactId>hbase-server</artifactId>
<version>1.1.2</version>
</dependency>

</dependencies>

创建HbaseDemo 类. 新建一个创建表的静态方法. 并测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
package com.dottie.hbase;

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.client.HBaseAdmin;
import org.apache.hadoop.hbase.util.Bytes;

import java.io.IOException;

/**
* Created by Daejong on 2018/1/20.
*/
public class HbaseDemo {

private static String TABLE_NAME = "t_student";
private static String ROWKEY = "rk001";
private static String FAMILY_INFO = "ContactInfo";
private static String FAMILY_PRIVATE = "PrivateInfo";
private static String COLUMN_NAME = "name";
private static String COLUMN_AGE = "age";
private static String COLUMN_GIRLFRIEND = "girlFriend";

public static void main(String[] args) throws Exception {
createTable();
}

public static void createTable() throws IOException {
// 创建一个默认的配置文件
Configuration conf = HBaseConfiguration.create();
// 创建一个hbaseadmin对象进行操作
HBaseAdmin hBaseAdmin = new HBaseAdmin(conf);
// 创建一个学生表
boolean tableExists = hBaseAdmin.tableExists(Bytes.toBytes(TABLE_NAME));
// 如果存在 就先删除该表
if (tableExists) {
hBaseAdmin.disableTable(Bytes.toBytes(TABLE_NAME));
hBaseAdmin.deleteTable(Bytes.toBytes(TABLE_NAME));
}
// 构造表
HTableDescriptor table = new HTableDescriptor(Bytes.toBytes(TABLE_NAME));
// 给表添加 两个列族
table.addFamily(new HColumnDescriptor(Bytes.toBytes(FAMILY_INFO)));
table.addFamily(new HColumnDescriptor(Bytes.toBytes(FAMILY_PRIVATE)));

// 创建表
hBaseAdmin.createTable(table);

// 关闭资源
hBaseAdmin.close();
}

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
启动hbase shell查看:
1. 输入list查看表
输出:
TABLE
t_student
2. 输入scan 't_student'
输出:
ROW COLUMN+CELL
0 row(s) in 0.1890 seconds
因为刚创建, 所以没有数据
3. 输入 describe 't_student' 表的具体信息
输出:
Table t_student is ENABLED
t_student
COLUMN FAMILIES DESCRIPTION
{NAME => 'ContactInfo', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA
_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE
=> '65536', REPLICATION_SCOPE => '0'}
{NAME => 'PrivateInfo', BLOOMFILTER => 'ROW', VERSIONS => '1', IN_MEMORY => 'false', KEEP_DELETED_CELLS => 'FALSE', DATA
_BLOCK_ENCODING => 'NONE', TTL => 'FOREVER', COMPRESSION => 'NONE', MIN_VERSIONS => '0', BLOCKCACHE => 'true', BLOCKSIZE
=> '65536', REPLICATION_SCOPE => '0'}
2 row(s) in 0.1520 seconds
可以看到有两个列族.

可见, 创建成功

获取表

1
2
3
4
5
6
7
8
public static HTable getTable() throws IOException {
Configuration conf = HBaseConfiguration.create();
HTable hTable = new HTable(conf, Bytes.toBytes(TABLE_NAME));
return hTable;
}

测试输出:
t_student;hconnection-0x12b0404f

增: 给表添加记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void putData() throws IOException {
// 获取到表
HTable table = getTable();
// 添加数据, 添加一个row key, 然后在添加具体信息
Put put = new Put(Bytes.toBytes(ROWKEY));
// 添加信息
put.add(Bytes.toBytes(FAMILY_INFO), Bytes.toBytes(COLUMN_NAME), Bytes.toBytes("daejong"));
put.add(Bytes.toBytes(FAMILY_INFO), Bytes.toBytes(COLUMN_AGE), Bytes.toBytes("22"));
put.add(Bytes.toBytes(FAMILY_PRIVATE), Bytes.toBytes(COLUMN_GIRLFRIEND), Bytes.toBytes("dottie"));
table.put(put);
// 关闭资源
table.close();
}

进入hbase shell查看:
scan 't_student'
输出:
ROW COLUMN+CELL
rk001 column=ContactInfo:age, timestamp=1516437553510, value=22
rk001 column=ContactInfo:name, timestamp=1516437553510, value=daejong
rk001 column=PrivateInfo:girlFriend, timestamp=1516437553510, value=dottie
1 row(s) in 0.1050 seconds

查: 根据rowkey查询一条记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void getData() throws IOException {
HTable table = getTable();
// 根据rowkey来查询
Get get = new Get(Bytes.toBytes(ROWKEY));
// 开始查看
Result result = table.get(get);
// 直接打印
for (Cell cell : result.rawCells()) {
System.out.println(Bytes.toString(CellUtil.cloneFamily(cell)));
System.out.println(Bytes.toString(CellUtil.cloneQualifier(cell)));
System.out.println(Bytes.toString(CellUtil.cloneValue(cell)));
}
}

输出:
ContactInfo
age
22
ContactInfo
name
daejong
PrivateInfo
girlFriend
dottie

创建命名空间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void createNameSpace() throws IOException {
Configuration conf = HBaseConfiguration.create();
HBaseAdmin hBaseAdmin = new HBaseAdmin(conf);

// 创建namespace ns1
NamespaceDescriptor ns1 = NamespaceDescriptor.create("ns1").build();
hBaseAdmin.createNamespace(ns1);

//关闭资源
hBaseAdmin.close();
}

输入: list_namespace
NAMESPACE
default
hbase
ns1
3 row(s) in 0.0980 seconds

查询表中所有数据

默认是根据rowkey和列族进行排序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public static void scanData() {
HTable table = null;
try {
table = getTable();
Scan scan = new Scan();
ResultScanner resultScanner = table.getScanner(scan);
for (Result rs : resultScanner) {
printResult(rs);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (table != null) {
IOUtils.closeStream(table);
}
}
}

public static void printResult(Result rs) {
for (Cell cell : rs.rawCells()) {
System.out.print(Bytes.toString(CellUtil.cloneRow(cell)) + " ");
System.out.print(Bytes.toString(CellUtil.cloneFamily(cell)) + " " );
System.out.print(Bytes.toString(CellUtil.cloneQualifier(cell)) + " ");
System.out.print(Bytes.toString(CellUtil.cloneValue(cell)));
System.out.println();
}
}

输出:
rk001 ContactInfo age 23
rk001 ContactInfo name dazhong
rk001 PrivateInfo girlFriend yao
rk002 ContactInfo age 22
rk002 ContactInfo name daejong
rk002 PrivateInfo girlFriend dottie

也可以指定rowkey区间查询:
scan.setStartRow(Bytes.toBytes("rk001"));
scan.setStopRow(Bytes.toBytes("rk002"));
注意区间是左闭右开.
rk001 ContactInfo age 23
rk001 ContactInfo name dazhong
rk001 PrivateInfo girlFriend yao
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

当然了, 可以指定列族进行查询
scan.addFamily(Bytes.toBytes(FAMILY_INFO));

输出:
rk001 ContactInfo age 23
rk001 ContactInfo name dazhong
rk002 ContactInfo age 22
rk002 ContactInfo name daejong

还有其他指定列column查询的....(类似的,省略)
以及还有设置缓存cache等等
scan.setCacheBlocks(false);
scan.setBatch(2);
scan.setCaching(2);

HBae的过滤器filter

指定查询条件 获取查询结果.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public static void hbaseFilter() {
HTable table = null;
try {
table = getTable();
Scan scan = new Scan();

// 创建一个filter
Filter filter = null;
// 查询rowkey 前缀中含有rk关键字. 关键字过滤
filter = new PrefixFilter(Bytes.toBytes("rk"));
// 分页查询过滤
// filter = new PageFilter(3);

// 创建一个 comparator 比较column为name的字段的值是否等于指定的值
// 类似: 模糊查询
ByteArrayComparable comparator = null;
comparator = new SubstringComparator("jong");
filter = new SingleColumnValueFilter(Bytes.toBytes(FAMILY_INFO),
Bytes.toBytes(COLUMN_NAME),
CompareFilter.CompareOp.EQUAL,
comparator);
scan.setFilter(filter);
// 查询数据, 返回结果集
ResultScanner resultScanner = table.getScanner(scan);
for (Result rs : resultScanner) {
printResult(rs);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (table != null) {
IOUtils.closeStream(table);
}
}
}

输出: (模糊查询)
rk002 ContactInfo age 22
rk002 ContactInfo name daejong
rk002 PrivateInfo girlFriend dottie

可以进行: 关键字过滤, 分页查询, 模糊查询等等.