首批通过分布式安全可靠测评,为关键业务系统打造
OBKV-Table 过期数据删除
更新时间:2026-07-23 19:56:22
OBKV-Table 提供了过期数据删除的功能,用户可以通过定义、配置和触发过期策略,减少存储空间占用。同时,OBKV-Table 也会对过期数据进行过滤,以屏蔽过期数据的可见性。本文介绍如何通过命令或周期任务删除过期数据。
注意
过期数据删除(TTL)功能仅限于 OceanBase KV 场景使用,SQL 场景下禁用,否则会导致不可预期的数据误删除问题。
使用说明
TTL 功能的基本使用流程如下:
- 开启 TTL 功能:设置
enable_kv_ttl = true开启租户的 TTL 任务开关。 - 定义 TTL 表:创建表时指定 TTL 属性,或对已有表添加 TTL 属性。支持修改/移除 TTL 属性。
- 触发删除任务:通过手动命令或设置周期任务触发过期数据删除。支持暂停/恢复/取消正在执行的任务。
- 查看任务状态:通过系统视图监控任务执行情况。
下面详细展开介绍。
第一步:开启 TTL 功能
开启 TTL 功能需要设置租户级配置项 enable_kv_ttl 为 true。
ALTER SYSTEM SET enable_kv_ttl= true; -- 租户级配置项,默认值为 false
第二步:定义 TTL 表
表的 TTL 属性通过 COLUMN_NAME + INTERVAL NUM TTL_UNIT 式定义,其中:
COLUMN_NAME:表中的已定义属性,类型为 TIMESTAMP 或 DATETIME。+ INTERVAL:固定部分。NUM:过期时间的整型。TTL_UNIT:可以是SECOND,MINUTE,HOUR,DAY,MONTH,YEAR中的任意一种。
如果是创建新的 TTL 关系表 t1,设置每行数据在 12 小时后会过期,可以通过以下语句创建:
CREATE TABLE t1 (
a INT PRIMARY KEY,
b TIMESTAMP DEFAULT current_timestamp ON UPDATE current_timestamp)
TTL (b + INTERVAL 12 HOUR);
如果是变更已有表 t1 的 TTL 属性,修改过期时间为 1 天,可以通过以下语句变更:
ALTER TABLE t1 TTL (b + INTERVAL 1 DAY);
如果是移除表 t1 的 TTL 属性,可以通过以下语句移除:
ALTER TABLE t1 REMOVE TTL;
第三步:任务触发
触发 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 任务的状态和相关信息:
| 视图名称 | 视图作用 |
|---|---|
| DBA_OB_KV_TTL_TASKS | 查看当前租户正在执行的 TTL 任务 |
| DBA_OB_KV_TTL_TASK_HISTORY | 查看当前租户的历史 TTL 任务 |
| CDB_OB_KV_TTL_TASKS | 系统租户下查看所有租户正在执行的 TTL 任务,比 DBA 视图多了 TENANT_ID 列 |
| CDB_OB_KV_TTL_TASK_HISTORY | 系统租户下查看所有租户的历史 TTL 任务,比 DBA 视图多了 TENANT_ID 列 |
任务状态说明
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;
索引扫描
TTL(Time To Live)支持通过索引扫描来提升过期数据清理的效率。在默认情况下,TTL 任务需对全表进行主键扫描并读取每一行数据来判断是否过期,这会带来较高的 I/O 开销。通过为过期列建立本地索引,并在 TTL 任务中指定使用该索引,只需扫描索引即可判断数据是否过期,从而减少无效的数据读取并显著提升清理速度。
在创建表时,通过属性参数 KV_ATTRIBUTES 的 TTLScanIndex 子参数指定索引名称来启用索引扫描,语法如下:
-- 实际使用中,$index_name 需要替换为索引名称
CREATE TABLE table_name (
...
) TTL (ttl_column + INTERVAL ...)
KV_ATTRIBUTES = '{"TTLScanIndex": "$index_name"}';
这里给出几个不同场景的示例:
示例 1 展示了 TTL 任务使用包含过期列的本地索引,来提升过期数据清理的效率:
CREATE TABLE test_ttl_with_index (
a INT PRIMARY KEY,
b TIMESTAMP DEFAULT current_timestamp ON UPDATE current_timestamp,
c VARCHAR(128),
INDEX idx(b) LOCAL
) TTL (b + INTERVAL 12 HOUR)
KV_ATTRIBUTES = '{"TTLScanIndex": "idx"}';
说明:
- 表使用
b列作为 TTL 列,过期时间为 12 小时。 - 通过
TTLScanIndex指定使用idx索引进行扫描。 idx索引建立在 TTL 列b上,可以高效定位过期数据。
当使用 OBKV-Table 的堆表模型时,一般会创建唯一索引作为唯一键,可以将过期列冗余存储在唯一索引上,TTL 任务指定该索引进行过期数据扫描。示例 2 展示了如何创建一个堆表,将过期列冗余存储在唯一索引上,并使用唯一索引进行过期数据扫描:
CREATE TABLE test_ttl_storing_index (
pagerowkey VARCHAR(1024) NOT NULL,
request LONGTEXT DEFAULT NULL,
rawpage LONGBLOB NOT NULL,
pagecode VARCHAR(1024) NOT NULL,
timestamp BIGINT(20) NOT NULL,
_expire_ts TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
-- 将 _expire_ts 冗余存储在唯一索引 u_idx_pagerowkey 上
UNIQUE INDEX u_idx_pagerowkey (pagerowkey) STORING (_expire_ts)
) ORGANIZATION = HEAP DEFAULT CHARSET = utf8mb4
TTL (_expire_ts + INTERVAL 1 DAY)
-- 使用唯一索引 u_idx_pagerowkey 进行过期数据扫描
KV_ATTRIBUTES = '{"TTLScanIndex": "u_idx_pagerowkey"}';
说明:
- 通过
STORING (_expire_ts)将 TTL 列_expire_ts冗余存储在唯一索引u_idx_pagerowkey上,避免在 TTL 任务中进行回表查询。
示例 3 展示了如何先创建表,后续通过 ALTER TABLE 添加索引 idx3 并启用索引扫描:
-- 创建表(初始不使用索引扫描)
CREATE TABLE test_ttl_with_index_6 (
a INT PRIMARY KEY,
b TIMESTAMP DEFAULT current_timestamp ON UPDATE current_timestamp,
c VARCHAR(128),
INDEX idx(b) LOCAL,
INDEX idx2(c) LOCAL
) TTL (b + INTERVAL 12 HOUR);
-- 添加索引并启用索引扫描
ALTER TABLE test_ttl_with_index_6 ADD INDEX idx3(b, c);
ALTER TABLE test_ttl_with_index_6 SET KV_ATTRIBUTES = '{"TTLScanIndex": "idx3"}';
说明:
- 通过
ALTER TABLE添加索引idx3并启用索引扫描。 - 通过
KV_ATTRIBUTES指定使用索引idx3进行过期数据扫描。
注意事项
过滤变更日志
注意
该功能从 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-Table 接口对某条记录进行操作,但是该条记录已经过期,这个时候的表现和该条记录不存在保持一致。例如,关系表中存在一条主键为 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,从而释放存储空间。
完整示例
启用 TTL 功能
ALTER SYSTEM SET enable_kv_ttl= true;创建一个 TTL 关系表,并定义每条记录在 c + 10s 之后过期。
CREATE TABLE ttl_table(a VARCHAR(1024) PRIMARY KEY, b VARCHAR(1024), c TIMESTAMP) TTL(c + INTERVAL 10 SECOND) PARTITION BY KEY(a) PARTITIONS 3;插入数据(可以使用 OBKV-Table/SQL,采用 OBKV-Table 接口插入数据,详细操作,请参见 客户端简介与使用说明)。
INSERT INTO ttl_table VALUES("k1", "hello obkv", now()); INSERT INTO ttl_table VALUES("k2", "hello obkv", now()); INSERT INTO ttl_table VALUES("k3", "hello obkv", now());等待 10s 之后,执行命令触发 TTL 任务执行。
ALTER SYSTEM TRIGGER TTL; -- 从 0 点开始,会尝试触发一次 TTL 任务查看任务执行状态。
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 | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+---------------+-------------+---------------------+----------+------------+ | ttl_table | 500002 | 200003 | 1 | 2023-09-27 23:31:30.300276 | 2023-09-27 23:31:35.315848 | USER | FINISHED | 2 | 0 | 2 | OB_SUCCESS | | ttl_table | 500002 | 200002 | 1 | 2023-09-27 23:31:30.300276 | 2023-09-27 23:31:35.320258 | USER | FINISHED | 1 | 0 | 1 | OB_SUCCESS | | ttl_table | 500002 | 200001 | 1 | 2023-09-27 23:31:30.300271 | 2023-09-27 23:31:35.321879 | USER | FINISHED | 0 | 0 | 0 | OB_SUCCESS | | NULL | -1 | -1 | 1 | 2023-09-27 23:31:28.675583 | 2023-09-27 23:31:28.675583 | USER | RS_TRIGGERING | 0 | 0 | 0 | OB_SUCCESS | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+---------------+-------------+---------------------+----------+------------+ 4 rows in set (0.043 sec)任务执行完成会移动到历史表中,可以通过
OCEANBASE.DBA_OB_KV_TTL_TASK_HISTORY表进行查看。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 | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+----------+-------------+---------------------+----------+------------+ | ttl_table | 500002 | 200003 | 1 | 2023-09-27 23:31:30.300276 | 2023-09-27 23:31:35.315848 | USER | FINISHED | 2 | 0 | 2 | OB_SUCCESS | | ttl_table | 500002 | 200002 | 1 | 2023-09-27 23:31:30.300276 | 2023-09-27 23:31:35.320258 | USER | FINISHED | 1 | 0 | 1 | OB_SUCCESS | | ttl_table | 500002 | 200001 | 1 | 2023-09-27 23:31:30.300271 | 2023-09-27 23:31:35.321879 | USER | FINISHED | 0 | 0 | 0 | OB_SUCCESS | | NULL | -1 | -1 | 1 | 2023-09-27 23:31:28.675583 | 2023-09-27 23:31:42.526853 | USER | FINISHED | 0 | 0 | 0 | OB_SUCCESS | +------------+----------+-----------+---------+----------------------------+----------------------------+--------------+----------+-------------+---------------------+----------+------------+ 4 rows in set (0.058 sec)任务执行完成之后,可以看到所有过期数据已经被删除。
obclient> SELECT * FROM ttl_table; Empty set (0.060 sec)