专栏首页SmartSiHBase Java Client API

HBase Java Client API

1. 概述

在这篇文章中,我们看一下 HBase Java 客户端 API 如何使用。HBase 用 Java 编写,并提供 Java API 与之通信。客户端 API 提供了DDL(数据定义语言)和DML(数据操作语言)语义,这与我们在关系数据库中的语义非常相似。因此,我们学习一下如何使用 HBase 的 Java 客户端 API 对 HBase 表进行 CRUD 操作。

2. Put

使用 Put 向表中插入数据。需要首先实例化 Connection 类,并通过 Connection 实例来实例化 Table 类:

Table getTable(TableName tableName)

插入数据需要实例化 Put 类,创建 Put 实例时用户需要提供一个行键 RowKey:

Put(byte[] row)
Put(byte[] row, long ts)

这两个 Put 实例都通过 row 参数指定了要插入的行。创建 Put 实例之后,就可以向该实例添加数据,添加数据方法如下:

Put addColumn(byte[] family, byte[] qualifier, byte[] value)
Put addColumn(byte[] family, byte[] qualifier, long ts, byte[] value)
Put addColumn(byte[] family, ByteBuffer qualifier, long ts, ByteBuffer value)

每一次调用 addColumn() 方法都可以添加一列数据,如果指定一个时间戳参数,就能形成一个数据单元格。如果不指定时间戳,Put 实例会使用来自构造函数的可选时间戳参数,如果用户在构造 Put 实例时也没有指定时间戳,则由 RegionServer 设定。

最后使用 Table 的 put() 方法向 HBase 中存储数据,使用如下方法进行调用:

void put(Put put) throws IOException

如下代码所示向 HBase 表中插入单行数据:

Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));
Put put = new Put(Bytes.toBytes("Lucy"));
put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes("28"));
table.put(put);
table.close();

HBaseConn 类是自定义的工具类,可以返回 Connection 类。

客户端 API 可以插入单个 Put 实例,同时也有批量处理操作的高级特性。调用形式如下:

void put(List<Put> puts) throws IOException

用户需要建立一个 Put 实例的列表,然后调用以列表为参数的 put() 方法。如下代码所示向 HBase 表中插入多行数据:

Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));

List<String> rowKeyList = Lists.newArrayList("Tom", "Kary", "Ford");
List<String> ageList = Lists.newArrayList("15", "43", "21");
List<Put> putList = Lists.newArrayList();
for (int index = 0;index < rowKeyList.size();index ++) {
    String rowKey = rowKeyList.get(index);
    String age = ageList.get(index);
    Put put = new Put(Bytes.toBytes(rowKey));
    put.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"), Bytes.toBytes(age));
    putList.add(put);
}
table.put(putList);
table.close();

3. Get

下面我们介绍根据客户端 API 查询已存储在 HBase 表中的数据。Table 类的 get() 方法可以从 HBase 表中读取数据。get() 方法需要 Get 类的实例。与 Put 类似,需要首先实例化 Connection 类,通过 Connection 实例来实例化 Table 类。

查询数据需要实例化 Get 类,创建 Get 实例时用户需要提供一个行键 RowKey:

Get(byte[] row)
Get(byte[] row, int rowOffset, int rowLength)

这两个 Get 实例都通过 row 参数指定了要查询的行。与 Put 操作一样,可以用多种标准筛选目标数据,也可以指定精确的坐标获取单元格的数据:

Get addFamily(byte[] family)
Get addColumn(byte[] family, byte[] qualifier)
Get setTimestamp(long timestamp)
Get setTimeRange(long minStamp, long maxStamp)
Get setFilter(Filter filter)
Get readVersions(int versions)
Get readAllVersions()

addFamily() 方法限制 get() 方法只能获取一个指定的列族,要获取多个列族需要多次调用。addColumn() 方法可以指定 get() 方法获取哪一列的数据。setTimestamp() 方法可以设定要获取的数据的时间戳,或者可以通过 setTimeRange() 方法设定一个时间段来获取某个时间戳段内的数据。如果用户没有设定时间戳,默认返回最新的匹配版本。

当用户使用 get() 方法获取数据时,HBase 返回的结果包含所有匹配的单元格数据,这些数据被封装在一个 Result 实例中返回给用户。用他提供的方法,可以从服务端获取匹配指定行的特定返回值,包括列族、列限定符以及时间戳等。如下代码所示从 HBase 表中查询单行数据:

Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));
Get get = new Get(Bytes.toBytes("Lucy"));
Result result = table.get(get);
byte[] value = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
System.out.println(Bytes.toString(value));

客户端 API 可以一次查询一行数据,同时也有批量处理操作的高级特性,用户可以一次请求获取多行数据。调用形式如下:

Result[] get(List<Get> gets) throws IOException

实际上,请求有可能被发往多个不同的服务器,但这部分逻辑已经被封装起来,因此对于客户端代码来说,还是表现为一次请求。

用户需要建立一个 Get 实例的列表,然后调用以列表为参数的 get() 方法,并返回一个 Result 数组。如下代码所示向 HBase 表中查询多行数据:

Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));
List<String> rowKeyList = Lists.newArrayList("Lucy", "Lily");
List<Get> getList = Lists.newArrayList();
for (int index = 0;index < rowKeyList.size();index ++) {
    String rowKey = rowKeyList.get(index);
    Get get = new Get(Bytes.toBytes(rowKey));
    getList.add(get);
}
Result[] results = table.get(getList);
for (Result result : results) {
    byte[] ageValue = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
    System.out.println(Bytes.toString(ageValue));
}
table.close();

4. Delete

下面我们介绍使用客户端 API 删除已存储数据的方法。Table 类的 delete() 方法可以从 HBase 表中删除数据。delete() 方法需要 Delete 类的实例。与 Put、Get 类似,都需要首先实例化 Connection 类,通过 Connection 实例来实例化 Table 类。

与前面讲的 get() 方法和 put() 方法一样,删除数据用户必须先创建一个 Delete 实例,并需要提供要删除行的行键 RowKey:

Delete(byte[] row)
Delete(byte[] row, long timestamp)
Delete(byte[] row, int rowOffset, int rowLength)
Delete(byte[] row, int rowOffset, int rowLength, long timestamp)

这几个 Delete 实例都通过 row 参数指定了要删除的行。我们最好缩小要删除的给定行中涉及的数据范围,可以使用下列方法:

Delete addFamily(byte[] family)
Delete addFamily(byte[] family, long timestamp)
Delete addFamilyVersion(byte [] family, long timestamp)
Delete addColumns(byte[] family, byte[] qualifier)
Delete addColumns(byte[] family, byte[] qualifier, long timestamp)
Delete addColumn(byte[] family, byte[] qualifier)
Delete addColumn(byte[] family, byte[] qualifier, long timestamp)

addFamily() 方法可以删除指定列族下所有的列(包括所有版本),我们也可以指定一个时间戳,触发针对单元格数据版本的过滤。从给定列族下的所有列中删除与给定时间戳相匹配的版本以及更旧版本的列。addFamilyVersion()addFamily() 方法不同的是,只会删除与时间戳相匹配的版本的所有列。 addColumns() 方法只作用于特定的一列,如果用户没有指定时间戳,这个方法会删除给定列的所有版本,如果指定了时间戳,从给定列中删除与给定时间戳相匹配的版本以及更旧的版本。addColumn()addColumns() 方法一样,也操作一个具体的列,但是只删除最新版本,保留旧版本。如果指定了时间戳,从给定列中删除与给定时间戳相匹配的版本。如下代码所示从 HBase 表中删除单行或者单列数据:

// 删除指定行数据
Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));
Delete delete = new Delete(Bytes.toBytes("Lucy"));
table.delete(delete);

// 删除指定列数据
Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));
Delete delete = new Delete(Bytes.toBytes("Lucy"));
delete.addColumn(Bytes.toBytes("info"), Bytes.toBytes("age"));
table.delete(delete);

客户端 API 可以一次删除一行数据,同时也有批量处理操作的高级特性,用户可以一次请求删除多行数据。调用形式如下:

void delete(List<Delete> deletes) throws IOException

用户需要建立一个 Delete 实例的列表,然后调用以列表为参数的 delete() 方法。如下代码所示向 HBase 表中删除多行数据:

Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));

List<String> rowKeyList = Lists.newArrayList("Lucy", "Lily");
List<Delete> deleteList = Lists.newArrayList();
for (int index = 0;index < rowKeyList.size();index ++) {
    String rowKey = rowKeyList.get(index);
    Delete delete = new Delete(Bytes.toBytes(rowKey));
    deleteList.add(delete);
}
table.delete(deleteList);
table.close();

5. Scan

在介绍基本的 CRUD 类型操作之后,现在来看一下 Scan 操作,类似于数据库系统中的游标,并利用到了 HBase 提供的底层顺序存储的数据结构。Scan 的工作类似于迭代器,所以用户无需调用 scan() 方法创建实例,只需要调用 Table 的 getScanner() 方法,这个方法在返回真正的扫描器实例的同时,用户可以使用它迭代获取数据。与 Put、Get、Delete 类似,需要首先实例化 Connection 类,通过 Connection 实例来实例化 Table 类。与前面讲过的 get()put() 以及 delete() 方法一样,用户必须先创建一个 Scan 实例:

Scan()
Scan(Get get)

创建 Scan 实例之后,用户可能还要给它增加很多限制条件。这种情况下,用户仍然可以使用空白参数的扫描,读取整个表格,包括所有的列族以及它们的所有列。可以使用多种方法限制所要读取的数据:

Scan addFamily(byte[] family)
Scan addColumn(byte[] family, byte[] qualifier)
Scan setTimestamp(long timestamp)
Scan setTimeRange(long minStamp, long maxStamp)
Scan withStartRow(byte[] startRow)
Scan withStopRow(byte[] stopRow)
Scan setFilter(Filter filter)

上述有几个方法于 Get 类相似的功能:可以使用 addFamily 方法限制返回数据的列族,或者通过 addColumn 方法限制返回的列。另外用户可以通过 setTimestamp 方法设置时间戳或者通过 setTimeRange 设置时间范围,进一步对结果进行限制。还可以使用 withStartRowwithStopRowsetFilter 进一步限定返回的数据。一旦设置好了 Scan 实例,就可以调用 Table 的 getScanner() 方法获取用于检索数据的 ResultScanner 实例。

Scan 操作不会通过一次 RPC 请求返回所有匹配的行,而是一行为单位进行返回。ResultScanner 把扫描操作转换为类似的 get 操作,它将每一行数据封装成一个 Result 实例,并将所有的 Result 实例放入一个迭代器中:

Connection connection = HBaseConn.create();
Table table = connection.getTable(TableName.valueOf("user"));
Scan scan = new Scan();
ResultScanner scanner = table.getScanner(scan);
for (Result result = scanner.next(); result != null; result = scanner.next()) {
    byte[] rowBytes = result.getRow();
    byte[] sexBytes = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("sex"));
    byte[] ageBytes = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("age"));
    byte[] addressBytes = result.getValue(Bytes.toBytes("info"), Bytes.toBytes("address"));
    String rowKey = Bytes.toString(rowBytes);
    String sex = Bytes.toString(sexBytes);
    String age = Bytes.toString(ageBytes);
    String address = Bytes.toString(addressBytes);
    System.out.println("rowKey: " + rowKey + ", cf: " + cf + ", sex: " + sex + ", age: " + age + ", address: " + address);
}
scanner.close();

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Flink1.4 状态终端

    状态可以存储在Java的堆内或堆外。根据你的状态终端,Flink 也可以管理应用程序的状态,这意味着 Flink 可以处理内存管理(可能会溢出到磁盘,如果有必要...

    smartsi
  • HBase Schema 设计

    HBase 与传统关系数据库(例如MySQL,PostgreSQL,Oracle等)在架构的设计以及为应用程序提供的功能方面有很大的不同。HBase 权衡了其中...

    smartsi
  • Redis使用Pipeline加速查询速度

    Redis 是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。这意味着通常情况下 Redis 客户端执行一条命令分为如下四个过程:

    smartsi
  • 『中级篇』集群服务间通信之RoutingMesh(47)

    PS:内部负载均衡 当在docker swarm集群模式下创建一个服务时,会自动在服务所属的网络上给服务额外的分配一个虚拟IP,当解析服务名字时就会返回这个虚拟...

    IT故事会
  • 拓扑数据分析与机器学习的相互促进

    用户1737318
  • 使用自己的语料训练word2vec模型

    先对新闻文本进行分词,使用的是结巴分词工具,将分词后的文本保存在seg201708.txt,以备后期使用。

    尾尾部落
  • ProxySQL Cluster 概述

    在 ProxySQL 1.4.2 之前,ProxySQL 单点的解决方法有配合 keepalived 使用来实现 ProxySQL 的主备,但是需要在主备上配置...

    wubx
  • 机器学习笔记—KNN算法

    目录[-] 前言 分类(Classification)是数据挖掘领域中的一种重要技术,它从一组已分类的训练样本中发现分类模型,将这个分类模型应用到待分类的样...

    jhao104
  • caffe安装成功

    在scalerstalk成长会机器学习小组群中,学过ng的机器学习视频后,来自中山大学的博士组长推荐我们下一步学习caffe,并看最新的deeplearning...

    努力在北京混出人样
  • Spring事务源码解析(二)获取增强

    在上一篇文章@EnableTransactionManagement注解解析中,我们搭建了源码阅读的环境,以及解析了开启Spring事务功能的注解@Enable...

    Java学习录

扫码关注云+社区

领取腾讯云代金券