首批通过分布式安全可靠测评,为关键业务系统打造
OBKV-HBase 过期数据删除
更新时间:2026-07-23 19:56:22
OBKV-HBase 实现了 HBase 模型中的列族(Column Family)级别和单元格(Cell)级别的过期数据删除(TTL,Time To Live)的能力,本文介绍如何通过命令或周期任务删除 Column Family 级别或 Cell 级别的过期数据。
注意
过期数据删除(TTL)功能仅限于 OceanBase KV 场景使用,SQL 场景下禁用,否则会导致不可预期的数据误删除问题。
分类
目前 OBKV-HBase 支持 Column Family 及 Cell 级别的 TTL。
| TTL 类型 | 描述 | 启用 | 版本支持 |
|---|---|---|---|
| Column Family 级别 | 设置 Column Family 级别的 TTL,所有写入该 Column Family 的数据在到达指定时间后会过期。 | 通过在建表语句中指定 KV_ATTRIBUTES 参数启用功能。 |
|
| Cell 级别 | Cell 级别的 TTL 允许用户为每个单元格设置独立的过期时间。这意味着即使在同一个 Column Family 中,不同的 Cell 可以有不同的存活时间。通过这种方式,用户可以更灵活地管理数据的生命周期,确保只有最相关的数据被保留。 | 通过建表时创建 TTL 列来启用功能,TTL 列的默认值为 NULL。 |
|
注意
可以同时启用 Column Family 级别和 Cell 级别的 TTL,实际的 TTL 值为二者中最小值。
使用说明
TTL 功能的基本使用流程如下:
- 开启 TTL 功能:设置
enable_kv_ttl = true开启租户的 TTL 任务开关。 - 定义 TTL 表:创建表时指定 TTL 属性(Column Family 级别或 Cell 级别)。支持修改/移除 TTL 属性。
- 触发删除任务:通过手动命令或设置周期任务触发过期数据删除。支持暂停/恢复/取消正在执行的任务。
- 查看任务状态:通过系统视图监控任务执行情况。
注意
Column Family 级别和 Cell 级别的 TTL 除了定义表语法有区别外,其他功能用法一致。
下面详细展开介绍。
第一步:开启 TTL 功能
开启 TTL 功能需要设置租户级配置项 enable_kv_ttl 为 true。
ALTER SYSTEM SET enable_kv_ttl= true; -- 租户级配置项,默认值为 false
第二步:定义 TTL 表
这里介绍 Column Family 级别和 Cell 级别的 TTL 定义表语法及示例。
Column Family 级别的 TTL 功能通过 KV_ATTRIBUTES 属性的 TimeToLive 和 MaxVersions 参数来设置。例如,创建一张 OBKV-HBase 表 t1$cf1,指定每行数据的过期时间为 3600 秒,每行数据的最大版本数量为 3。
CREATE TABLE t1$cf1 (
K VARBINARY(1024),
Q VARBINARY(256),
T BIGINT,
V VARBINARY(1048576) NOT NULL,
PRIMARY KEY(K, Q, T))
-- TimeToLive 单位为秒
KV_ATTRIBUTES ='{"Hbase": {"TimeToLive": 3600, "MaxVersions": 3}}'
PARTITION BY KEY(K) PARTITIONS 97;
修改 OBKV-HBase 表 t1$cf1,移除 TimeToLive 属性,修改最大版本数量为 4。
ALTER TABLE t1$cf1 KV_ATTRIBUTES ='{"Hbase": {"MaxVersions": 4}}';
移除 OBKV-HBase 表 t1$cf1 的过期属性。
ALTER TABLE t1$cf1 KV_ATTRIBUTES ='{"Hbase": {}}';
Cell 级别的 TTL 功能通过在表中创建一个 TTL 列来设置。例如,创建一张 OBKV-HBase 表 t1$cf1,包含 TTL 列。
CREATE TABLE `t1$cf1` (
`K` VARBINARY(1024) NOT NULL,
`Q` VARBINARY(256) NOT NULL,
`T` BIGINT(20) NOT NULL,
`V` VARBINARY(1024) DEFAULT NULL,
-- 创建 Cell 级别 TTL,具体参数在执行 put/increment/append/get/scan/delete DML 操作时指定,单位为毫秒
-- TTL 列必须创建在第 5 列的位置
`TTL` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`K`, `Q`, `T`)
);
第三步:任务触发
触发 TTL 删除任务有两种方式:
- 管控命令触发:需登录到系统租户,用于触发所有租户的 TTL 任务。除了触发任务,系统租户还支持暂停、恢复和取消正在执行的 TTL 任务。
- 周期性任务触发:需登陆到您需要执行 TTL 任务的用户租户,设置该用户租户每天的 TTL 任务触发时间。
TTL 命令只在用户租户或系统租户下执行有效。在用户租户下执行,只能管控本租户的 TTL 任务;在系统租户下执行,可以管控所有用户的 TTL 任务。
登录到系统租户,并执行如下命令,触发所有租户的 TTL 任务。
ALTER SYSTEM TRIGGER TTL;
暂停正在执行的 TTL 任务,命令如下:
ALTER SYSTEM SUSPEND TTL;
恢复暂停的 TTL 任务,命令如下:
ALTER SYSTEM RESUME TTL;
取消正在执行的 TTL 任务,命令如下:
ALTER SYSTEM CANCEL TTL;
登录到您需要执行 TTL 任务的租户,执行如下命令设置租户每天 TTL 任务的触发时间。
-- 默认值为 "",表示不触发周期性 TTL 任务
ALTER SYSTEM SET kv_ttl_duty_duration = '[22:00:00, 24:00:00]';
第四步:查看任务状态
通过视图可以查看当前以及历史 TTL 任务的状态和相关信息,与 TTL 任务相关的视图有四张。具体请参见OBKV-HBase 相关视图。
任务类型
当前 OBKV-HBase 的 TTL 任务有两种类型:Normal TTL 任务和 Hbase Rowkey TTL 任务,对应任务状态表中的 task_type 列。
- Normal TTL 任务
通过命令或者周期性任务触发,首先生成一个租户级的 TTL 任务(对应 table id 为 -1 的那条记录),同时对所有的定义了过期规则的表,生成 tablet 级 TTL 任务并执行,每个 tablet 任务会对该 tablet 进行全扫描来找出并删除过期数据,当所有的 tablet 任务执行完成之后,会将这次租户 TTL 任务的所有任务记录移动到历史表中。
- HBase Rowkey TTL 任务
固定周期性的触发,每天会将旧的 HBase rowkey TTL 任务记录移动到历史表中,生成一个新的租户级别的 HBase rowkey TTL 任务(对应 table id 为 -2),并对所有的定义了过期规则的表生成 tablet 级 TTL 任务,该 tablet 任务执行需要基于用户请求触发,会收集 Scan/Get 操作中涉及到的过期 row(即 TTL 或 MaxVersion 过期的 row),并生成只扫描该 row 的 TTL 任务,任务执行后会将执结果(scan_cnt/max_version_del_cnt/ttl_del_cnt)汇总到对应的 tablet 任务上。
任务状态说明
TTL 任务分为租户级任务和 Tablet 级任务两类,对应不同的状态,均可在任务视图 DBA_OB_KV_TTL_TASKS 和 CDB_OB_KV_TTL_TASKS 以及历史任务视图 DBA_OB_KV_TTL_TASK_HISTORY 和 CDB_OB_KV_TTL_TASK_HISTORY 中查看。
注意
租户级任务的 TABLE_NAME 为 NULL,TABLET_ID、TABLE_ID 均为 -1。
Tablet 级任务状态:
| 任务状态 | 说明 |
|---|---|
| PREPARED | 准备状态,还未开始删除过期数据 |
| RUNNING | 运行状态,正在删除该分区过期数据 |
| PENDING | 暂停状态,可以通过命令恢复执行 |
| CANCELED | 取消状态,无法恢复执行 |
| FINISHED | 完成状态,待移动到历史表中 |
| INVALID | 非法状态 |
租户级任务状态:
| 任务状态 | 说明 |
|---|---|
| RS_TRIGGERING | 表示租户 TTL 任务正在执行中 |
| RS_SUSPENDING | 表示租户 TTL 任务已经暂停 |
| RS_CANCELING | 表示租户 TTL 任务已经取消 |
| RS_MOVING | 表示正在移动所有的 TTL 任务记录到历史表 |
| INVALID | 非法状态 |
设置历史任务清理周期(可选)
为了避免 TTL 历史任务堆积造成不必要的存储空间浪费,可以通过 kv_ttl_history_recycle_interval 配置项设置租户的历史 TTL 任务记录的保存时长(默认为 7 天)。
例如,设置自动清理超过 30 天的历史 TTL 任务记录,命令如下:
ALTER SYSTEM SET kv_ttl_history_recycle_interval= '30d';
性能优化
设置并行度
可以通过租户级配置项 ttl_thread_score 设置 TTL 任务的工作线程数量,默认值为 2。
例如,修改 TTL 任务的工作线程数量为 10:
ALTER SYSTEM SET ttl_thread_score = 10;
注意事项
过滤变更日志
注意
该功能从 V4.4.2 BP1 版本开始支持,需要搭配 V4.4.2 BP1 及以上版本的 liboblog 使用。
TTL 任务在删除过期数据时会产生大量变更日志(Change Log,CLOG)。默认情况下,CDC(liboblog)会正常捕获并消费这些变更日志,再同步给下游组件。在过期数据量较大的场景下,TTL 删除过期数据产生的大量日志会给 CDC 链路带来额外的处理压力及资源开销。
为减轻 CDC 链路资源压力,可开启 CDC 侧 TTL 变更日志过滤能力(filter_ttl_delete_log)。开启后,TTL 任务产生的变更日志会被 CDC 过滤,不再向下游输出。默认不启用过滤,即 TTL 变更日志会被 CDC 正常消费;开启过滤后,仅影响 TTL 任务变更日志,不改变 TTL 任务本身的执行逻辑。
过期数据的可见性
如果使用 OBKV-HBase 接口对某条记录进行操作,但是该条记录已经过期,这个时候的表现和该条记录不存在保持一致。例如,关系表中存在一条主键为 k1 的过期记录,这个时候再插入一条主键为 k1 的记录会成功,而不是报主键冲突。
| 操作 | 过期 | 返回码 | affected rows | 其他 |
|---|---|---|---|---|
| insert | N | -5024 OB_ERR_PRIMARY_KEY_DUPLICATE |
0 | |
| insert | Y | 0 OB_SUCCESS |
1 | |
| delete | N | 0 OB_SUCCESS |
1 | |
| delete | Y | 0 OB_SUCCESS |
0 | |
| update | N | 0 OB_SUCCESS |
1 | |
| update | Y | 0 OB_SUCCESS |
0 | |
| replace | N | 0 OB_SUCCESS |
2 | |
| replace | Y | 0 OB_SUCCESS |
1 | |
| insert_or_update | N | 0 OB_SUCCESS |
1 | |
| insert_or_update | Y | 0 OB_SUCCESS |
1 | |
| increment | N | 0 OB_SUCCESS |
1 | |
| increment | Y | 0 OB_SUCCESS |
1 | 除了 increment 列其他列会被写成默认值 |
| append | N | 0 OB_SUCCESS |
1 | |
| append | Y | 0 OB_SUCCESS |
1 | 除了 append 列其他列会被写成默认值 |
| query/get | N | 0 OB_SUCCESS |
/ | 成功读到数据 |
| query/get | Y | 0 OB_SUCCESS |
/ | 读不到数据 |
说明
SQL 接口暂时不会对过期数据进行屏蔽,在过期数据被删除任务清理之前,过期数据都是可见的。
任务执行机制
TTL 过期删除任务是一个后台低优先级任务,暂无法保证过期数据立即删除。为了不对系统性能造成较大影响,在以下情况下任务会自动暂停:
- 内存压力:当 MemStore 内存
MEMSTORE_USED超过阈值FREEZE_TRIGGER时,任务会暂停执行,直到内存降低到阈值以下。可通过视图 GV$OB_MEMSTORE 查看内存使用情况。 - 物理恢复期间:对于正在进行物理恢复的租户,TTL 任务会暂停执行,直到租户完成恢复。
存储空间释放
由于 OceanBase 数据库采用 LSM-Tree 架构,删除操作本质上是一次追加写入(写入删除标记)。因此,TTL 任务删除过期数据后,实际存储空间不会立即释放,甚至可能暂时增加。存储空间的最终释放依赖于 Major Compaction 过程。
为了确保存储空间能够及时释放,建议将 TTL 任务与 Major Freeze 配合使用:
- 设置 TTL 任务的执行时间窗口(kv_ttl_duty_duration)。
- 将 Major Freeze 的执行时间(major_freeze_duty_time)设置在 TTL 任务窗口之后。
这样可以在 TTL 任务完成后自动触发 Major Compaction,从而释放存储空间。
完整示例
Column Family 级别
启用 TTL 功能。
ALTER SYSTEM SET enable_kv_ttl= true;创建一个 OBKV-HBase 表。
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": {"TimeToLive": 3600, "MaxVersions": 3}}' PARTITION BY KEY(K) PARTITIONS 3;模拟 OBKV-HBase 插入数据。
实际插入数据过程请参考 OBKV-HBase 客户端使用介绍,示例如下:
INSERT INTO t1$cf1 VALUES ("row1", "cq1", -truncate(time_to_usec(date_sub(now(), interval +1 hour))/1000, 0), "del"), ("row1", "cq1", -truncate(time_to_usec(now())/1000, 0), "no del"), ("row2", "cq1", -truncate(time_to_usec(date_sub(now(), interval +1 hour))/1000, 0), "del"), ("row2", "cq1", -truncate(time_to_usec(now())/1000, 0), "no del"), ("row3", "cq1", -truncate(time_to_usec(date_sub(now(), interval +1 hour))/1000, 0), "del"), ("row3", "cq1", -truncate(time_to_usec(now())/1000, 0), "no del"), ("row4", "cq1", -truncate(time_to_usec(date_sub(now(), interval +1 hour))/1000, 0), "del"), ("row4", "cq1", -truncate(time_to_usec(now())/1000, 0), "no del");设置租户每日 TTL 任务时间,自动触发任务。
-- 从 0 点开始,会尝试触发一次 TTL 任务 ALTER SYSTEM SET kv_ttl_duty_duration = '[00:00:00, 23:59:59]';查看任务执行状态。
查看当前任务(执行结果取决于实际任务执行状态)。
obclient> SELECT * FROM oceanbase.dba_ob_kv_ttl_tasks;返回结果如下:
+------------+----------+-----------+---------+----------------------------+----------------------------+--------------+---------------+-------------+---------------------+----------+------------+ | TABLE_NAME | TABLE_ID | TABLET_ID | TASK_ID | START_TIME | END_TIME | TRIGGER_TYPE | STATUS | TTL_DEL_CNT | MAX_VERSION_DEL_CNT | SCAN_CNT | RET_CODE | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+---------------+-------------+---------------------+----------+------------+ | NULL | -1 | -1 | 1 | 2023-09-28 11:35:02.695292 | 2023-09-28 11:35:17.720627 | PERIODIC | RS_TRIGGERING | 0 | 0 | 0 | OB_SUCCESS | | t1$cf1 | 500002 | 200001 | 1 | 2023-09-28 11:35:06.612708 | 2023-09-28 11:35:06.612708 | PERIODIC | PREPARED | 0 | 0 | 0 | OB_SUCCESS | | t1$cf1 | 500002 | 200002 | 1 | 2023-09-28 11:35:06.612711 | 2023-09-28 11:35:06.612711 | PERIODIC | PREPARED | 0 | 0 | 0 | OB_SUCCESS | | t1$cf1 | 500002 | 200003 | 1 | 2023-09-28 11:35:06.612712 | 2023-09-28 11:35:06.612712 | PERIODIC | PREPARED | 0 | 0 | 0 | OB_SUCCESS | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+---------------+-------------+---------------------+----------+------------+ 4 rows in set查看历史任务(执行结果取决实际任务执行状态)。
obclient> SELECT * FROM oceanbase.dba_ob_kv_ttl_task_history;返回结果如下:
+------------+----------+-----------+---------+----------------------------+----------------------------+--------------+----------+-------------+---------------------+----------+------------+ | TABLE_NAME | TABLE_ID | TABLET_ID | TASK_ID | START_TIME | END_TIME | TRIGGER_TYPE | STATUS | TTL_DEL_CNT | MAX_VERSION_DEL_CNT | SCAN_CNT | RET_CODE | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+----------+-------------+---------------------+----------+------------+ | NULL | -1 | -1 | 1 | 2023-09-28 11:35:02.695292 | 2023-09-28 11:35:17.720627 | PERIODIC | FINISHED | 0 | 0 | 0 | OB_SUCCESS | | t1$cf1 | 500002 | 200001 | 1 | 2023-09-28 11:35:06.612708 | 2023-09-28 11:35:11.634029 | PERIODIC | FINISHED | 1 | 0 | 2 | OB_SUCCESS | | t1$cf1 | 500002 | 200002 | 1 | 2023-09-28 11:35:06.612711 | 2023-09-28 11:35:11.632463 | PERIODIC | FINISHED | 1 | 0 | 2 | OB_SUCCESS | | t1$cf1 | 500002 | 200003 | 1 | 2023-09-28 11:35:06.612712 | 2023-09-28 11:35:11.627921 | PERIODIC | FINISHED | 2 | 0 | 4 | OB_SUCCESS | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+----------+-------------+---------------------+----------+------------+ 4 rows in set
查看当前 OBKV-HBase 表中的数据。
obclient> SELECT * FROM t1$cf1;返回结果如下:
+------+-----+----------------+--------+ | K | Q | T | V | +------+-----+----------------+--------+ | row3 | cq1 | -1695872081000 | no del | | row4 | cq1 | -1695872081000 | no del | | row1 | cq1 | -1695872081000 | no del | | row2 | cq1 | -1695872081000 | no del | +------+-----+----------------+--------+ 4 rows in set
Cell 级别
创建一张 OBKV-HBase 表 t1$cf1,包含 TTL 列。
CREATE TABLE `t1$cf1` (
`K` VARBINARY(1024) NOT NULL,
`Q` VARBINARY(256) NOT NULL,
`T` BIGINT(20) NOT NULL,
`V` VARBINARY(1024) DEFAULT NULL,
-- 创建 Cell 级别 TTL,具体参数在执行 put/increment/append/get/scan/delete DML 操作时指定,单位为毫秒
-- TTL 列必须创建在第 5 列的位置
`TTL` BIGINT(20) DEFAULT NULL,
PRIMARY KEY (`K`, `Q`, `T`)
);
数据操作示例如下:
put 操作
Put put1 = new Put(key1.getBytes()); put1.addColumn(family.getBytes(), column1.getBytes(), toBytes(11L)); put1.addColumn(family.getBytes(), column2.getBytes(), toBytes(11L)); # 该 put 操作中每个 Cell 的 TTL 被设置为 300 毫秒,意味着它们在 300 毫秒后会被视为过期。 # 过期数据的删除要依赖 TTL 后台任务的执行。如果没有开启 Rowkey TTL 任务,在过期数据被转储之前,你仍然可以通过 SQL 查询到这些过期的 Cell;如果开启了 Rowkey TTL 任务,那么在下一次扫描前,可以通过 SQL 查询到这些 Cell 的数据,但不能通过 get 操作获取,因为 get 操作会自动过滤掉过期的 Cell。 put1.setTTL(300); hTable.put(put1);increment 操作
Increment increment = new Increment(key1.getBytes()); increment.addColumn(family.getBytes(), column1.getBytes(), 1L); increment.addColumn(family.getBytes(), column2.getBytes(), 2L); # 如果 increment 没有设置 TTL,则 increment 出来的新 Cell 使用的 TTL 值就是查询出的 Cell 的值 # 如果 increment 设置了 TTL,那么 increment 出来的新 Cell 的 TTL 值就是 increment 设置的值 # 该 increment 操作结束后,在 500 毫秒以内查询,预期能查询到新插入的 Cell;500 毫秒后查询该表,则预期查询不到。 increment.setTTL(500); hTable.increment(increment);append 操作
Append append = new Append(key1.getBytes()); append.add(family.getBytes(), column1.getBytes(), toBytes(11L)); append.add(family.getBytes(), column2.getBytes(), toBytes(11L)); # 同 increment 操作 append.setTTL(500); hTable.append(append);get/scan 操作
get/scan 操作不支持设置 TTL,但在查询时会自动过滤过期的 Cell。同时,扫描的 Cell 会记录其 Rowkey 的过期状态。如果 Rowkey 中有 Cell 过期,将触发 Rowkey TTL,及时清理过期的 Cell。
delete 操作
delete 操作有
setTTL接口,设置了 TTL 的 delete 操作会报错。它会删除未过期的最新版本。如果最新版本已过期,则会删除第二个版本的 Cell。例如,当最新版本的 Cell 设置了 TTL 并过期后,执行 delete 操作将删除下一个版本的 Cell。部分示例代码如下:hTable.put(put1); hTable.put(put2); Delete del = new Delete(key1.getBytes()); del.addColumn(family.getBytes(), column1.getBytes()); hTable.delete(del);执行 delete 操作前表数据如下:
+------+-----+----------------+--------+--------+ | K | Q | T | V | TTL | +------+-----+----------------+--------+--------+ | key1 | cf1 | -1741157319257 | value | 5 | | key1 | cf1 | -1741157319182 | value | NULL | +------+-----+----------------+--------+--------+ 2 rows in set执行 delete 操作后表数据如下:
+------+-----+----------------+--------+--------+ | K | Q | T | V | TTL | +------+-----+----------------+--------+--------+ | key1 | cf1 | -1741157319257 | value | 5 | +------+-----+----------------+--------+--------+ 1 row in set这里可以看到,删除了第二个版本的数据,没删除过期的数据。