---
title: "OBKV-HBase 数据模式 - OceanBase 数据库 V4.3.5 | OceanBase 文档中心"
description: OBKV-HBase 数据模式 本文介绍 OBKV-HBase 数据库对象的创建和管理。 注意 在进行数据库对象操作之前，确保您已经创建了 MySQL 租户。 数据库 OBKV 集群其实就是一个 MySQL 租户的 OceanBase 集群。库（Database） 是数据库对象的集合，用于权限管理和命名空间隔离。 在…
---
切换语言

- 中文站 - 简体中文
- International - English
- 日本站 - 日本語

文档反馈![](https://mdn.alipayobjects.com/huamei_22khvb/afts/img/A*P8CuR4UJ_FkAAAAAAAAAAAAADiGDAQ/original) OceanBase 数据库KV 型 - V 4.3.5 LTS

# OBKV-HBase 数据模式

更新时间：2026-04-15 16:01:32

[编辑](https://github.com/oceanbase/oceanbase-kv/edit/V4.3.5/zh-CN/200.obkv-hbase/700.obkv-hbase-reference/100.obkv-hbase-schema-design.md)  

本文介绍 OBKV-HBase 数据库对象的创建和管理。

#### 注意

在进行数据库对象操作之前，确保您已经创建了 MySQL 租户。

## 数据库

OBKV 集群其实就是一个 MySQL 租户的 OceanBase 集群。库（Database） 是数据库对象的集合，用于权限管理和命名空间隔离。

在创建数据表之前，需要先在 MySQL 租户下创建数据库。有关数据库创建和管理的详细操作参加：

- [创建数据库](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576014)
 - [查看数据库](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576015)
 - [修改数据库](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576013)
 - [删除数据库](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576016)
 - [CREATE DATABASE](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001577343)

## 表组

表组（Table Group）是一个逻辑概念，表示一组表的集合。默认情况下，不同表之间的数据是随机分布的，没有直接关系。通过定义表组，可以控制一组表在物理存储上的邻近关系。  
 OBKV-HBase 在实现 HBase 模型的时候，将 HBase 的表映射为 OceanBase 数据库中的表组。假设 HBase 有一张 htable1 的表，在 OceanBase 数据库中, 需要创建一个 htable1 的表组。

#### 注意

一个表组下所有表的分区规则要一致。

在创建数据表之前，需要先创建对应的表组。

```
CREATE TABLEGROUP htable1;

```

关于表组的详细介绍和操作指导，参见：

- [关于表组](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576022)
 - [创建表组](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576019)
 - [查看表组信息](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576017)
 - [将表添加到表组](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576021)
 - [修改表组的 SHARDING 属性](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576018)
 - [管理表组内的表](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576023)
 - [删除表组](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576020)
 - [CREATE TABLEGROUP](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001577358)

## 表

OBKV-HBase 将 HBase 的 Column Family 映射为 OceanBase 中的一张关系表。在使用 HBase 之前，一定要先在数据库中建表组以及表。

### 表识别标志

对于 OBKV-HBase 表，该功能主要有以下两个作用：

- 识别 OBKV-HBase 表类型。如果是 OBKV-HBase 表，则可以识别该表支持[分区分裂功能](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002503436)。
 - 启用[列族（Column Family）级别的 TTL 功能](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002022355)。

#### 注意

强烈建议在建表时添加 `kv_attributes` 设置，以便系统能够准确识别 OBKV 表类型。

OBKV-HBase 类型的 `kv_attributes` 目前仅支持 `TimeToLive` 和 `MaxVersions` 两个设置参数，支持两个参数支持任意顺序设置、设置任意一个或均不设置。

示例如下：

```sql
-- 设置 TimeToLive 和 MaxVersions，这两个参数仅支持正整数类型值
CREATE TABLE t1$cf1 (
  K VARBINARY(1024),
  Q VARBINARY(256),
  T BIGINT,
  V VARBINARY(1048576) NOT NULL,
  PRIMARY KEY(K, Q, T))
-- '{"HBase": {xxx}}' 该形式不可变更，HBase/TimeToLive/MaxVersions 大小写不敏感
kv_attributes ='{"HBase": {"TimeToLive": 3600, "MaxVersions": 3}}'
PARTITION BY KEY(K) PARTITIONS 97;

-- 仅设置 MaxVersions
CREATE TABLE t1$cf1 (
  K VARBINARY(1024),
  Q VARBINARY(256),
  T BIGINT,
  V VARBINARY(1048576) NOT NULL,
  PRIMARY KEY(K, Q, T))
kv_attributes ='{"HBase": {"MaxVersions": 3}}'
PARTITION BY KEY(K) PARTITIONS 97;

-- kv_attributes 为空
CREATE TABLE t1$cf1 (
  K VARBINARY(1024),
  Q VARBINARY(256),
  T BIGINT,
  V VARBINARY(1048576) NOT NULL,
  PRIMARY KEY(K, Q, T))
kv_attributes ='{"HBase": {}}'
PARTITION BY KEY(K) PARTITIONS 97;

```

### 普通表

普通表是 OBKV-HBase 的最基本的数据模型。假设 HBase 有一张 `htable1` 的表，有一个列族 `family1`。在 OceanBase 数据库中，需要创建一个 `htable1` 的表组以及一张普通表 `htable1$family1`,其中普通表的命名规则为 `TableGroupName$FamilyName`，并和 `htable1` 的表组进行绑定。

#### 注意

OBKV-HBase Put、Delete、Get、Scan 接口支持多列族，示例请见[数据操作示例](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002022353)。

```sql
CREATE TABLEGROUP htable1;

CREATE TABLE htable1$family1 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V varbinary(1048576) NOT NULL,
  PRIMARY KEY(K, Q, T))
TABLEGROUP =  htable1
PARTITION BY KEY(K) PARTITIONS 97;

CREATE TABLE htable1$family2 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V varbinary(1048576) NOT NULL,
  PRIMARY KEY(K, Q, T))
TABLEGROUP =  htable1
PARTITION BY KEY(K) PARTITIONS 97;

```

虽然 OBKV-HBase 兼容了 HBase Schemless 的特性，但是基于 OceanBase 数据库实现的任何模型都有 Schema。上述建表语句，就是 OBKV-HBase 的物理存储模型。

参数说明：

- htable1 为建立的 HBase 表名，可自行命名。
 - family1、family2 为列族名，可自行命名。  

  #### 说明

  表名与列族名之间用美元符号（$）连接，作为 OceanBase 数据库的表名。
 - K 列存储 HBase 表的 Rowkey。
 - Q 列存储 Column Qualifier。
 - T 列存储时间戳版本 TimeStamp，为自 1970-01-01 UTC 至今的毫秒。
 - V 列存储 value，使用了 varbinary 类型，最长 1 MB，如果长度不够，可以用 longblob 类型，但使用 longblob 会有 10% 左右的性能损耗。
 - K、Q、T 组成联合主键，标识 HBase 模型的一个 cell。

#### 注意

- 必须按照 K, Q, T, V 进行命名，不能自行修改。
 - 当前版本仅支持一级分区。

通过上述 SQL 建表，一个 HBase 表中行的若干列数据，在关系表中存储为相邻的若干行，每一行存储 HBase 的一个 cell：`<row, column family, column qualifier, timestamp, value>`。

### 时序表

从 V4.3.5 BP2 版本开始，OBKV-HBase 支持了时序表的存储，时序表适用于时序场景。

#### 注意

- 时序表模型由 K、T、S、V、G 几列组成，列名不可更改。
 - 仅支持一级分区。

限制如下：

- 目前只支持 put、get、scan、delete 接口。每个接口都有一定限制，具体如下：
     - put 接口限制：
           - 不支持 Cell 级别 TTL。
           - 不支持多列族。
     - get 接口限制：
           - 不支持 filter。
           - 不支持多列族。
           - 不支持 maxVersion，可设置但不会生效。
           - 不支持 isCheckExistenceOnly。
           - 不支持 isClosestRowBefore。
           - 不支持 Rowkey TTL。
     - scan 接口限制：
           - 不支持 filter。
           - 不支持多列族。
           - 不支持 maxVersion，可设置但不会生效。
           - 不支持 ReverseScan。
           - 不支持 limit。
           - 不支持 setBacth。
           - 不支持 setCaching。
           - 不支持 setMaxResultSize。
           - 不支持 setRowOffsetPerColumnFamily。
           - 不支持 setCacheBlocks。
           - 不支持 Rowkey TTL。
     - delete 接口限制：
           - 不支持多列族。

#### 创建时序表

```sql
CREATE TABLEGROUP htable1;

CREATE TABLE `htable1$family1` (
  `K` varbinary(1024) NOT NULL,
  `T` bigint(20) NOT NULL,
  `S` bigint(20) NOT NULL,
  `V` json NOT NULL,
  `G` bigint(20) GENERATED ALWAYS AS (ABS(T)),
  PRIMARY KEY (`K`, `T`, `S`)
) PARTITION BY KEY(`K`) PARTITIONS 97;

CREATE TABLE `htable1$family2` (
  `K` varbinary(1024) NOT NULL,
  `T` bigint(20) NOT NULL,
  `S` bigint(20) NOT NULL,
  `V` json NOT NULL,
  `G` bigint(20) GENERATED ALWAYS AS (ABS(T)),
  PRIMARY KEY (`K`, `T`, `S`)
) PARTITION BY KEY(`K`) PARTITIONS 97;

```

参数说明：

- K 列存储 HBase 表的 Rowkey，可用作分区键。
 - T 列存储时间戳版本 TimeStamp，为自 1970-01-01 UTC 至今的毫秒。
 - S 列用于存储与同一个 K 和 T（时间戳）对应的多次不同写入的序列号。通常情况下，这个序列号是写入操作的时间戳。S 列在写入数据时无需指定，系统自动生成。
 - V 列将多个 Q（Qualify） 和 V（value） 编码到 JSON 中。
 - G 列是一个虚拟生成列，用于存储 T（时间戳）的绝对值（ABS），可作为时间维度的分区键。
 - K、T、S 组成联合主键。

#### 注意

必须按照 K, T, S, V, G 进行命名，不能自行修改。

## Lob

HBase 是一个 KV 类的数据库，用户的数据都序列化为 Binary 的形式，数据有可能非常大，OceanBase 数据库支持不超过 512M 的大对象。

众所周知，大对象的 DML 性能都比较低，在使用的时候尽量避免使用大对象。正常情况下，建议在定义 Schema 的时候，V 列使用 varbinary 类型，最长 1M。如果用户实际场景不能避免操作超过 1M 的大对象，可以定义 V 列为 longblob 类型。

```
create table htable1$family1 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V longblob NOT NULL,
  primary key(K, Q, T))
TABLEGROUP =  htable1
partition by key(K) partitions 97;

```

## 索引

表创建成功后，可以在表的一个或多个列上创建索引以加速表上的语句执行速度。索引使用正确的话，可以减少物理 IO 或者逻辑 IO。

OceanBase 数据库支持全局索引和局部索引、唯一索引和非唯一索引等。

#### 注意

OBKV-HBase 暂时不支持二级索引。

有关索引类型和使用的详细介绍，参见：

- [索引简介](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576025)
 - [创建索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576029)
 - [在分区表上建立索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576958)
 - [查看索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576027)
 - [监控索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576024)
 - [删除索引](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576026)

## 分区

上述建表过程中没有包含分区方式，建立的表是一个非分区表，仅适用于数据量很小的场景。一般情况下 HBase 业务表数据量都比较大，所以我们需要建立分区表。OBKV-HBase 当前仅支持一级分区策略，支持 Range 分区和 Key 分区。

一级分区的 Range/Range Columns 分区支持自动分区分裂，这种分区方式适合范围扫描的场景，同时它还有普通 Range 分区所不具有的自动按照数据量分裂的能力，将数据量进行均匀分布。更多请参见 [OBKV-HBase 自动分区分裂](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000002503436)。

关于分区的详细介绍，参见 [分区类型](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576880)。

### 一级分区策略

当前仅支持一级分区，可以参考如下流程决定具体使用哪一种分区方法。

#### Key 分区

如果您的业务中没有用 Scan 进行范围扫描，或者说您只需要 Get 接口，那么使用 Key 分区。分区数需要是奇数，最好是素数，建议值为 97、193、389 等。Key 分区的显著优点是数据分布均匀，一般不会出现数据倾斜和热点。示例如下：

```
CREATE TABLE htable1$family1 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V varbinary(1048576) NOT NULL,
  PRIMARY KEY(K, Q, T))
  TABLEGROUP =  htable1
PARTITION BY KEY(K) PARTITIONS 97;

```

#### 虚拟列结合 Key 分区

如果您在业务中必须使用 Scan，但是扫描范围总是 Rowkey 的一个定长前缀（前缀扫描），可以在 K 上定义一个虚拟列，然后对这个虚拟列进行 Key 分区。通过这样的方式，可以保证每次扫描只涉及一个分区。除非前缀数据有倾斜，这种分区方式也有利于避免热点。示例如下：

```
CREATE TABLE htable1$family1 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V varbinary(1048576),
  K_PREFIX varbinary(1024) GENERATED ALWAYS AS (substring(K, 1, 4)),
  PRIMARY KEY(K, Q, T))
  TABLEGROUP =  htable1
PARTITION BY KEY(K_PREFIX) PARTITIONS 97;

```

参数说明：函数 `substring(K, 1, 4)` 表示取 K（即 HBase 表的 Rowkey）的前 4 个字节为子串，这里的 “4” 需要根据业务 Rowkey 特点，替换为前缀长度。

#### 说明

当前只支持使用 substring 构建虚拟列。

#### Range 分区

如果您不仅需要前缀扫描和 Get，那么必须建立 Range 分区。建议您需要预先对 Rowkey 的分布进行估计或采样，选定 Range 分区的分割点。如果预估不合理，这种分区方式很容易造成数据倾斜和热点，因尽量避免采用这种方式。示例如下：

```
CREATE TABLE htable1$family1 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V varbinary(1048576),
  PRIMARY KEY(K, Q, T)
)
TABLEGROUP =  htable1
PARTITION BY RANGE columns (K)
(
  PARTITION p0 VALUES LESS THAN ('a'),
  PARTITION p1 VALUES LESS THAN ('w'),
  PARTITION p2 VALUES LESS THAN MAXVALUE);

```

参数说明：  
 通过 'a' 和 'w' 把整个 Rowkey 值域空间分为 3 段。

### 分区管理

关于分区的详细操作指导，参见：

- [创建分区表](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576046)
 - [修改分区规则](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576053)
 - [添加分区](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576047)
 - [删除分区](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576049)
 - [TRUNCATE 分区](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576055)
 - [分区裁剪](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576048)

## 主键设计

### Key 分区键的设计

在 OBKV-HBase 中，分区键的设计对性能的提升非常重要。众所周知，在分布式系统中，为了保证分布式事务的 ACID 以及分布式读的快照一致性读，需要付出额外的性能代价，这也是很多开源 NoSQL 数据库不支持跨行事务，以及不提供全局快照一致性读的原因。

基于性能考虑，OBKV-HBase 不支持分布式事务，也不支持全局一致性快照读。但是支持节点级别的事务（开源 HBase 只支持单行事务），以及节点级别的一致性快照读。通过分区键的设计，可以将业务的请求，限制在一个分区内，可以做到既能满足扩展性的诉求，又能得到一个单机的性能以及一致性。

分区键是主键的前缀，如下举一个例子，比如是一个用户消息系统：

- 每个用户有个定长 64 位唯一 ID，userid
 - 一条消息，有个 64 位的消息 ID，msgid

主键设计为 userid | msgid，分区键定义如下：

```
CREATE TABLE htable1$family1 (
  K varbinary(1024),
  Q varbinary(256),
  T bigint,
  V varbinary(1048576),
  K_PREFIX varbinary(1024) GENERATED ALWAYS AS (substring(K, 1, 64)),
  PRIMARY KEY(K, Q, T))
  TABLEGROUP =  htable1
  PARTITION BY KEY(K_PREFIX) PARTITIONS 97;

```

如上，通过 substring 从 K 列的主键中提取了定长的分区键，一个 userid 的所有 scan/get/put/Batch 等都会访问一个分区，在单分区内，OBKV-HBase 会保证跨行的事务，以及一致性读。

### Key 分区数的设计考虑

#### 为什么要关注分区数

- 分区数的调整是 DDL，重新调整有一定的代价。
 - 分区数过小不是太合适：举个例子，DB 的均衡算法会保证两个节点之间分区数的差异不超过 1，假设 3 个节点，7 个分区，那么三个节点上的分区数是 <3,2,2>，也即节点间分区的不均衡度为 (3-2)/3 = 33%。在假定没有数据倾斜的条件下，分区数几乎正比于数据的存储量以及访问量，分区的不均衡度也会体现在各个节点资源使用上的不均衡。
 - 分区数过大也不是太合适：举个例子，分区类似 miniDB，也就是一个 DB 中有多个 miniDB。每个 miniDB 独立管理自己的存储空间和数据访问，首先 miniDB 多会导致存储空间不能极致共享，另外 Scan 可能会跨多个 DB，有一定的性能损失，业务上的 Batch 操作难以直接下压给存储做极致的性能优化。

#### 分区数对性能的影响

- 一般情况，分区的大小对性能的影响不大。
 - 主要影响的是合并/迁移/备份等基于分区为粒度任务，分区过大，会导致单个任务的执行时间长，也会对空间的要求会更高。

#### 分区数如何选择

- 如果基于容量考虑，我们建议单分区单副本的数据量尽量不超过 100GB。在选取分区数的时候，尽量选择 23/59/97/193/389/997 等素数。比如三副本，24T 的存储空间占用，单分区单副本 8T，预估需要至少 80 个分区，基于上述推荐值，选择 97 个分区。
 - 分区数的变更是 DDL，有一定的开销。在设计分区数的时候，尽量避免频繁做分区数变更，要提前做好分区规划。假设当前的存储空间是 24T，但是未来一年，预期存储空间会扩大到 50T，那么在初始设计的时候，可以基于 50T 为基准，最好选择 193 的分区数。
 - 分区数最好不要选择过大，最大不建议超过 997。

#### 如何判断分区间的数据倾斜

Key 分区目前使用 MurmurHash，会基于分区键把数据 Hash 后，投递到对应的分区。除非有极端情况，比如数据 1 亿条，1kw的数据都是一个分区键，这种会造成比较严重的数据倾斜。如果数据 1 亿条，有部分分区键下有几千条数据，这种场景一般不会造成倾斜，因为在大样本下，经过 Hash 后算分区键，有比较大的随机性，从全局看，各个分区的数据应该是接近。

### 主键的常见设计考虑

#### 主键打散

在一个分布式系统中，主键打散至关重要。请求均衡打散在集群中各个机器上，可以最大化利用整个分布式系统的算力。

- 如果选择 Range 分区，主键前缀最好不要采用自增列或者时间列。比如一个消息系统的主键设计为 timestamp | msgid，就不是一个好的设计，会导致写入请求都几乎集中在一台机器上。
 - 尽量避免使用一个有很大基数的前缀作为主键前缀，比如一个消息系统的主键设计为 msgtype | msgid， 就不是一个好的设计（msgtype 的枚举数量较少），也会导致读写请求几乎都集中在一台机器上。

#### 主键设计

- 主键长度不宜过长，最好不要超过 1K。过长的主键会导致冗余存储，另外会导致访问性能不必要的下降，典型的是主键不能用大对象类型。
 - 如果使用 Range 分区，但是主键前缀需要是一个自增列或者时间列，会导致数据访问热点。可以考虑把这个自增的主键前缀 hash 后作为新的主键前缀，比如一个不优的主键设计 timestamp | msgid，可以考虑将主键变为 hashvalueoftime | timestamp | msgid。在使用 Key 分区的时候，Key 分区会将分区键 hash 打散到各个分区中，可以不用提取前缀 Hash。

## 相关文档

- 关于 OceanBase 数据库对象的详细介绍，参见 [数据库对象介绍](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001573824) 章节。
 - 关于 OceanBase 数据库对象的操作指导，参见 [数据库对象管理](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001576014) 章节。
 - 关于 OceanBase 数据库的设计规范和约束，参见 [数据库设计规范和约束](https://www.oceanbase.com/docs/common-oceanbase-database-cn-1000000001574171) 章节。

 上一篇 下一篇 ![有帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*y6ocSqN8cqsAAAAAAAAAAAAAARQnAQ)![无帮助](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*BG9IQJyLHF8AAAAAAAAAAAAAARQnAQ)![反馈](https://gw.alipayobjects.com/mdn/ob_asset/afts/img/A*eTWdQKCRKHwAAAAAAAAAAAAAARQnAQ)[AI](https://www.oceanbase.com/obi) 咨询热线
