首批通过分布式安全可靠测评,为关键业务系统打造
OBKV-HBase 数据操作示例
更新时间:2026-04-15 16:01:32
本文以具体的示例为例来介绍如果通过 OBKV-HBase 客户端进行数据处理。
说明
OBKV-HBase 客户端已支持的接口列表,参见兼容 HBase 的接口。
准备工作
基于以下示例进行体验之前,请确保您已经完成如下准备工作:
已经部署了 OceanBase 集群。关于支持的部署方案、部署方式以及详细的部署操作,参见 部署简介。
已经创建了 MySQL 租户。关于创建租户的详细操作,参见 创建租户。
已经创建了数据库。关于创建数据库的详细操作,参见 创建数据库。
已经创建了 OBKV-HBase 数据表。关于创建 OBKV-HBase 数据表的详细操作,参见 数据库模式设计。
在本示例中,表组名为 htable1。每个表对应一个列族,可按需建表。建表示例如下。
CREATE TABLEGROUP htable1; CREATE TABLE htable1$family1 ( `K` varbinary(1024) NOT NULL, `Q` varbinary(256) NOT NULL, `T` bigint(20) NOT NULL, `V` varbinary(1024) DEFAULT NULL, PRIMARY KEY (`K`, `Q`, `T`) ) TABLEGROUP = htable1 PARTITION BY KEY(`K`) PARTITIONS 3; CREATE TABLE htable1$family2 ( `K` varbinary(1024) NOT NULL, `Q` varbinary(256) NOT NULL, `T` bigint(20) NOT NULL, `V` varbinary(1024) DEFAULT NULL, PRIMARY KEY (`K`, `Q`, `T`) ) TABLEGROUP = htable1 PARTITION BY KEY(`K`) PARTITIONS 3; CREATE TABLE htable1$family3 ( `K` varbinary(1024) NOT NULL, `Q` varbinary(256) NOT NULL, `T` bigint(20) NOT NULL, `V` varbinary(1024) DEFAULT NULL, PRIMARY KEY (`K`, `Q`, `T`) ) TABLEGROUP = htable1 PARTITION BY KEY(`K`) PARTITIONS 3;本示例通过 OHTable 连接 OBKV-HBase 集群并进行数据处理,请确保已经完成 OBKV-HBase 客户端的部署和配置,并通过 OHTable 连接了集群。详细操作参见 使用 OBKV-HBase 客户端连接集群。
注意事项
- 代码示例中的执行结果,依赖下面各个操作的顺序,如需复现,请按照顺序执行。
- 为了比较直观的观察数据库中的结果,示例中采用 MySQL 客户端连接 OBKV-HBase,通过 SQL 来查询数据库表的状态。
注意
OBKV-HBase 的 put、delete、get、scan 接口支持多列族。
put
函数说明:
put 操作允许一行中指定多列,支持多列族,多列族的写入可以保证原子性。
未指定版本(时间戳)的情况下,本次写入列的版本是服务端执行时获取的时间戳,多个列族中写入列的版本一致。
函数原型:
- void put(Put put)
- void put(List
puts)
参数列表:
- put:put 对象,用于插入单条记录。
- puts:put 对象集合,用于批量插入多条记录。
操作示例:
Put put = new Put("testKey0".getBytes());
put.add("family1".getBytes(), "column1".getBytes(), "value1-1".getBytes());
put.add("family1".getBytes(), "column2".getBytes(), "value1-2".getBytes());
put.add("family2".getBytes(), "column3".getBytes(), "value2-3".getBytes());
put.add("family3".getBytes(), "column4".getBytes(), "value3-4".getBytes());
hTable.put(put);
MySQL [test]> SELECT * FROM htable1$family1;
+----------+---------+----------------+---------+
| K | Q | T | V |
+----------+---------+----------------+---------+
| testKey0 | column1 | -1730184389941 | value1-1|
| testKey0 | column2 | -1730184389941 | value1-2|
+----------+---------+----------------+---------+
2 rows in set
MySQL [test]> SELECT * FROM htable1$family2;
+----------+---------+----------------+---------+
| K | Q | T | V |
+----------+---------+----------------+---------+
| testKey0 | column3 | -1730184389941 | value2-3|
+----------+---------+----------------+---------+
1 row in set
MySQL [test]> SELECT * FROM htable1$family3;
+----------+---------+----------------+---------+
| K | Q | T | V |
+----------+---------+----------------+---------+
| testKey0 | column4 | -1730184389941 | value3-4|
+----------+---------+----------------+---------+
1 row in set
get
函数说明:
- 获取指定行的数据。
函数原型:
- Result get(final Get get)
- Result[] get(List
gets)
参数列表:
get:get 对象,需要使用 addColumn 或 addFamily 函数来指定操作目标。可以在单行 get 操作中任意组合并操作多个列族。返回值按 K 列 / Q 列字典序排序。
- addColumn:
- 用于指定要检索的特定列。
- 需要提供列族和列限定符。
- 适用于需要精确获取某个列的数据的场景。
- 只会返回指定列的值。
- addFamily:
- 用于指定要检索的整个列族。
- 只需要提供列族的名称。
- 适用于需要获取整个列族中所有列的数据的场景。
- 会返回该列族中所有列的值。
- addColumn:
gets:get 对象列表,保存 get 对象。
操作示例:
// 获取 family
int maxVersion = 1;
Get get = new Get("testKey0".getBytes());
get.addColumn("family1".getBytes(), "column1".getBytes());
get.addFamily("family2".getBytes());
Result result = hTable.get(get);
for (Cell cell : result.rawCells()) {
System.out.println(new String(CellUtil.cloneRow(cell)) + " "
+ new String(CellUtil.cloneFamily(cell)) + " "
+ new String(CellUtil.cloneQualifier(cell)) + " "
+ new String(CellUtil.cloneValue(cell)));
}
## 打印结果如下
## testKey0 family1 column1 value1-1
## testKey0 family2 column3 value2-3
## 上述 Get 操作转换为如下 SQL 查询
MySQL [test]> SELECT * FROM htable1$family1 WHERE k = "testKey0" AND Q = 'column1';
+----------+---------+----------------+---------+
| K | Q | T | V |
+----------+---------+----------------+---------+
| testKey0 | column1 | -1730184389941 | value1-1|
+----------+---------+----------------+---------+
1 row in set
MySQL [test]> SELECT * FROM htable1$family2 WHERE k = "testKey0";
+----------+---------+----------------+---------+
| K | Q | T | V |
+----------+---------+----------------+---------+
| testKey0 | column3 | -1730184389941 | value2-3|
+----------+---------+----------------+---------+
1 row in set
scan
函数说明:
- 根据指定条件(scan/family/qualifier)对 Table 进行扫描。
函数原型:
- ResultScanner getScanner(byte[] family, byte[] qualifier)
- ResultScanner getScanner(final byte[] family)
- ResultScanner getScanner(final Scan scan)
参数列表:
- family:目标列簇,这里是指需要过滤的列簇。
- qualifier:目标列名,这里指需要过滤的列名。
- scan:Scan 对象,使用 addFamily 或 addColumn 等设置操作目标。
- addColumn:
- 用于指定要检索的特定列。
- 需要提供列族和列限定符。
- 适用于需要精确获取某个列的数据的场景。
- 只会返回指定列的值。
- addFamily:
- 用于指定要检索的整个列族。
- 只需要提供列族的名称。
- 适用于需要获取整个列族中所有列的数据的场景。
- 会返回该列族中所有列的值。
- addColumn:
操作示例:
Scan scan = new Scan("testKey0".getBytes());
scan.addColumn("family1".getBytes(), "column1".getBytes());
scan.addFamily("family2".getBytes());
scan.setStartRow("testKey0".getBytes());
ResultScanner scanner = hTable.getScanner(scan);
for (Result r : scanner) {
for (Cell cell : r.rawCells()) {
System.out.println(new String(CellUtil.cloneRow(cell)) + " "
+ new String(CellUtil.cloneFamily(cell)) + " "
+ new String(CellUtil.cloneQualifier(cell)) + " "
+ new String(CellUtil.cloneValue(cell)));
}
}
## 打印结果如下
## testKey0 family1 column1 value1-1
## testKey0 family2 column3 value2-3
## 上述 Scan 操作转换为如下 SQL 查询
MySQL [test]> SELECT *
-> FROM htable1$family2
-> WHERE K >= "testKey0"
-> UNION
-> SELECT *
-> FROM htable1$family1
-> WHERE K >= "testKey0" AND Q = "column1";
+----------+---------+----------------+---------+
| K | Q | T | V |
+----------+---------+----------------+---------+
| testKey0 | column1 | -1730184389941 | value1-1|
| testKey0 | column3 | -1730184389941 | value2-3|
+----------+---------+----------------+---------+
2 rows in set
OBKV-HBase 支持 Reverse Scan 功能,它允许用户以行键(RowKey)逆序(从大到小)扫描表中的数据,这与常规的正序(从小到大)扫描相反。
正序扫描示例如下:
-- 正序扫描
String startKey = "testKey";
String endKey = "testKey9";
String family = "family";
String column = "column";
int maxVersion = 1;
Scan scan = new Scan();
scan.addColumn(family.getBytes(), column.getBytes());
scan.setMaxVersions(maxVersion);
scan.setStartRow(startKey.getBytes());
scan.setStopRow(endKey.getBytes());
ResultScanner scanner = hTable.getScanner(scan);
for (Result r : scanner) {
for (KeyValue kv : r.list()) {
System.out.printf("Scan Demo: Rowkey: %s, Column Family: %s, Column Qualifier: %s, Value: %s, Timestamp: %d%n",
Bytes.toString(r.getRow()),
Bytes.toString(kv.getFamily()),
Bytes.toString(kv.getQualifier()),
Bytes.toString(kv.getValue()),
kv.getTimestamp());
}
}
返回结果如下:
+----------+--------+----------------+----------+
| K | Q | T | V |
+----------+--------+----------------+----------+
| testKey0 | column | -1715961561057 | putValue |
| testKey1 | column | -1715961561132 | putValue |
| testKey2 | column1 | -1715961561132 | putValue |
| testKey2 | column2 | -1715961561132 | putValue |
+----------+--------+----------------+----------+
4 rows in set
逆序扫描示例如下:
-- 逆序扫描
String startKey = "testKey9";
String endKey = "testKey";
String family = "family";
String column = "column";
int maxVersion = 1;
Scan scan = new Scan();
scan.addColumn(family.getBytes(), column.getBytes());
scan.setMaxVersions(maxVersion);
scan.setStartRow(startKey.getBytes());
scan.setStopRow(endKey.getBytes());
// 设置启用 Reverse scan
scan.setReversed(true);
ResultScanner scanner = hTable.getScanner(scan);
for (Result r : scanner) {
for (KeyValue kv : r.list()) {
System.out.printf("Scan Demo: Rowkey: %s, Column Family: %s, Column Qualifier: %s, Value: %s, Timestamp: %d%n",
Bytes.toString(r.getRow()),
Bytes.toString(kv.getFamily()),
Bytes.toString(kv.getQualifier()),
Bytes.toString(kv.getValue()),
kv.getTimestamp());
}
}
返回结果如下:
+----------+--------+----------------+----------+
| K | Q | T | V |
+----------+--------+----------------+----------+
| testKey2 | column1 | -1715961561132 | putValue |
| testKey2 | column2 | -1715961561132 | putValue |
| testKey1 | column | -1715961561132 | putValue |
| testKey0 | column | -1715961561057 | putValue |
+----------+--------+----------------+----------+
4 rows in set
incrementColumnValue
函数说明:
- 对表中的某行单列数据进行自增,执行成功则返回自增后的值,指定列的 cell 必须是 long 类型(64 字节整型)。
函数原型:
- long incrementColumnValue(byte[] row, byte[] family, byte[] qualifier, long amount, boolean writeToWAL)
参数列表:
- row:行键。
- family:目标列簇,这里是建表时指定的列簇。
- qualifier:目标列名。
- amount:增加的数量(可以为负值)。
- writeToWAL:是否预写日志,OBKV-HBase 不需要设置。
操作示例:
// 这里展示的是对单列自增 1
String column = "incrementColumn";
String key = "incrementKey";
String family = "family";
long increment_value = 1L;
long ret = hTable.incrementColumnValue(
key.getBytes(),
family.getBytes(),
column.getBytes(),
increment_value);
System.out.printf("Increment Column Demo: ret: %b%n", ret);
MySQL [test]> select *,hex(v) from htable1$family where k = "incrementKey";
+--------------+-----------------+----------------+----------+------------------+
| K | Q | T | V | hex(v) |
+--------------+-----------------+----------------+----------+------------------+
| incrementKey | incrementColumn | -1715961669857 | | 0000000000000001 |
+--------------+-----------------+----------------+----------+------------------+
备注:
- 如果使用 MySQL 客户端查看 HBase 表,可能会发现对应 Cell 的 V 字段没有值,可以用 hex(v) 来显示。
- 如上,Increment 的行为是如果数据库中查询不到满足条件的 Cell,那么会生成一个 Cell,值默认为 0,接着进行 Increment。
increment
函数说明:
- 对指定行中的单列或多列进行增加,指定列的 cell 必须是 long 类型(64 字节整型)。
函数原型:
- Result increment(Increment increment)
参数列表:
- increment: Increment 类型,需要使用 addColumn 进行属性设置。
操作示例:
// 这里展示的是对单列自增 1
String column = "incrementColumn";
String key = "incrementKey";
String family = "family";
long increment_value = 1L;
Increment increment = new Increment(key.getBytes());
increment.addColumn(family.getBytes(), column.getBytes(), increment_value);
Result r = hTable.increment(increment);
for (KeyValue kv : r.list()) {
System.out.printf("Increment Demo: Rowkey: %s, Value:%s%n",
Bytes.toString(r.getRow()),
Bytes.toLong(kv.getValue()));
}
MySQL [test]> select *,hex(v) from htable1$family where k = "incrementKey";
+--------------+-----------------+----------------+----------+------------------+
| K | Q | T | V | hex(v) |
+--------------+-----------------+----------------+----------+------------------+
| incrementKey | incrementColumn | -1715961734681 | | 0000000000000002 |
| incrementKey | incrementColumn | -1715961669857 | | 0000000000000001 |
+--------------+-----------------+----------------+----------+------------------+
备注:
- 如上,对一个 Cell 多次做自增,会产生多个版本的 Cell。HBase 中没有 Update 的操作语义,对一个 Cell 的修改会变成多版本,用户可以指定版本来做查询。
append
函数说明:
- 对指定行中的单列或多列进行增加(针对字符类型,例如 byte,string 等)。
函数原型:
- Result append(Append append)
参数列表:
- append:Append 类型,需要使用 add 函数进行属性设置。
操作示例:
String column = "appendColumn";
String key = "appendKey";
String family = "family";
Append append = new Append(key.getBytes());
append.add(family.getBytes(), column.getBytes(), toBytes("_append"));
Result r = hTable.append(append);
for (KeyValue kv : r.list()) {
System.out.printf("Appand Demo: Rowkey: %s, Append Value:%s%n",
Bytes.toString(r.getRow()),
Bytes.toString(kv.getValue()));
}
MySQL [test]> select * from htable1$family where k = "appendKey";
+-----------+--------------+----------------+---------+
| K | Q | T | V |
+-----------+--------------+----------------+---------+
| appendKey | appendColumn | -1715961265748 | _append |
+-----------+--------------+----------------+---------+
备注:
- 如上,Append 的行为是如果数据库中查询不到满足条件的 Cell,那么会插入对应的 Cell,值默认为空串,接着对生成的记录进行 Append 操作。
delete
函数说明:
- 删除指定的存储单元(cells)或行(rows)。
函数原型:
- void delete(Delete delete)
- void delete(List
deletes)
参数列表:
- delete:Delete 对象,这里是指需要执行删除的行,通过 deleteColumn、deleteColumns、deleteFamily 函数设置。可以在单行 Delete 操作中任意组合并操作多个列族。
- deleteColumn:
- 用于删除特定版本的单个列。
- 需要指定列族、列限定符和时间戳。
- 适用于需要删除某个特定版本的数据的场景。
- deleteColumns:
- 用于删除一个列的所有版本。
- 只需要指定列族和列限定符。
- 适用于需要删除某个列的所有历史数据的场景。
- deleteFamily:
- 用于删除指定列族中的所有列及其所有版本的数据。
- 只需要指定列族的名称。
- 适用于需要清空某个列族的所有数据的场景。
- deleteColumn:
操作示例:
// 删除 family1 所有的列和 family2 的 column3 列
// 此时再次 Get,只能查到 family1 和 family2 的 column3 列以外的值
Delete delete = new Delete(toBytes("testKey0"));
delete.deleteFamily("family1".getBytes());
delete.deleteColumns("family2".getBytes(), "column3".getBytes());
hTable.delete(delete);
// 删除 testKey0 所有的列,此时再次 Get 无法查询到结果
Delete delete = new Delete(toBytes("testKey0"));
hTable.delete(delete);
exists
函数说明:
- 判断在 Get 对象中设置的列簇或列是否存在,存在则返回 true,反之返回 false。
函数原型:
- boolean exists(Get get)
参数列表:
- get:Get 对象,使用 addFamily 或 addColumn 来设置操作目标。
操作示例:
String key = "testKey2";
String family = "family";
Get get = new Get(key.getBytes());
get.addFamily(family.getBytes());
boolean ret = hTable.exists(get);
System.out.printf("Exist Demo: ret: %b%n", ret);
MySQL [test]> select * from htable1$family where k = "testKey2";
+----------+--------+----------------+----------+
| K | Q | T | V |
+----------+--------+----------------+----------+
| testKey2 | column | -1715961561132 | putValue |
+----------+--------+----------------+----------+
备注:
- Exist 语义等同于 Get。
checkAndPut
函数说明:
- 检查并替换指定列数据,如果条件满足并替换成功,返回 true,反之返回 false。
函数原型:
- boolean checkAndPut(byte[] row, byte[] family, byte[] qualifier, byte[] value, Put put)
参数列表:
- row:目标行键。
- family:目标列簇。
- qualifier:目标列名。
- value:目标列值。
- put:需要替换的列值。
操作示例:
String key = "testKey2";
String family = "family";
String column = "column";
String value = "putValue";
String new_value = "value_new";
Put put = new Put(key.getBytes());
put.add(family.getBytes(), column.getBytes(), new_value.getBytes());
boolean ret = hTable.checkAndPut(
key.getBytes(),
family.getBytes(),
column.getBytes(),
value.getBytes(),
put);
System.out.printf("CheckAndPut Demo: ret: %b%n", ret);
MySQL [test]> select * from htable1$family where k = "testKey2";
+----------+--------+----------------+-----------+
| K | Q | T | V |
+----------+--------+----------------+-----------+
| testKey2 | column | -1715961408587 | value_new |
| testKey2 | column | -1715961035471 | putValue |
+----------+--------+----------------+-----------+
备注:
- 将满足条件的,主键为 testKey2,值为 putValue 的Cell,做了修改,生成一个值为 value_new 的新版本的 Cell。
checkAndDelete
函数说明:
- 匹配删除列,若匹配条件符合并删除成功,返回 true,反之返回 false。
函数原型:
- boolean checkAndDelete(byte[] row, byte[] family, byte[] qualifier, byte[] value, Delete delete)
参数列表:
- row:目标匹配行键。
- family:目标列簇,这里是建表时指定的列簇。
- qualifier:目标匹配列名。
- value:目标匹配列值。
- delete:预期需要删除的列。
操作示例:
String key = "testKey2";
String family = "family";
String column = "column";
String value = "value_new";
Delete delete = new Delete(key.getBytes());
delete.deleteColumn(family.getBytes(), column.getBytes());
boolean ret = hTable.checkAndDelete(
key.getBytes(),
family.getBytes(),
column.getBytes(),
value.getBytes(),
delete);
System.out.printf("CheckAndDelete Demo: ret: %b%n", ret);
MySQL [test]> select * from htable1$family where k = "testKey2";
+----------+--------+----------------+----------+
| K | Q | T | V |
+----------+--------+----------------+----------+
| testKey2 | column | -1715961035471 | putValue |
+----------+--------+----------------+----------+
备注:
- 将满足条件的,主键为 testKey2,值为 value_new 的 Cell,做了删除,数据库中不存在这个 Cell。
getConfiguration
函数说明:
- 返回操作实例中的 config 句柄,可用于在操作中变更属性。
函数原型:
- Configuration getConfiguration()
参数列表:
- 无
操作示例:
hTable.getConfiguration().set("rpc.execute.timeout", "1500");