news 2026/9/10 21:21:58

TiDB TTL 表设计与实现全解:行级到期数据自动清理的语法、调度架构与源码剖析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
TiDB TTL 表设计与实现全解:行级到期数据自动清理的语法、调度架构与源码剖析

TiDB TTL 表设计与实现全解:行级到期数据自动清理的语法、调度架构与源码剖析

【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb

TTL(Time To Live)表是 TiDB 提供的一种"数据自动过期清理"能力:为表指定一个时间列与保留时长后,后台任务会周期性扫描并删除已过期的行,广泛适用于验证码、会话、日志、事件流水等只需短期保留数据的场景。本文以 TTL 表设计提案 为骨架,结合当前仓库中pkg/ttl的实现源码,完整讲解 TTL 表的 DDL 语法、表级配置、后台作业调度(Job/Task)架构、扫描与删除执行细节、系统变量与监控指标,并给出可复现的 SQL 示例与源码级证据,帮助读者既能"会用",也能理解"如何实现"。

一、TTL 表要解决什么问题

在传统数据库中,清理"过期数据"通常依赖业务方定期执行DELETE,或由 DBA 编写定时脚本按时间字段分批删除。这类做法有两个痛点:删除语句容易与在线业务互相影响,且"多久删一次、删哪些行"完全游离于表结构之外,无法被数据库统一管理与运维。

TTL 表把"过期清理"变成一种表级声明式属性。一个 TTL 表包含一个类型为DATE/DATETIME/TIMESTAMP的时间列,该列与当前时间比较,当二者间隔超过设定的阈值(如 3 个月)时,对应行会被自动删除。如设计文档引言所述,其典型场景就是清理"过期的验证码"这类数据——写入后只需要存活有限时间,之后由数据库自动回收,业务代码无需再关心删除逻辑。

二、总体设计选型:为什么采用 SQL 层方案

设计文档明确了 TiDB TTL 的核心理念是SQL-layer approach:后台任务通过 SQL 协议扫描并删除过期行。相比把 TTL 下沉到存储层,这一方案的优势被明确列出:

  • 实现简单,天然感知表结构与 SQL 语义;
  • 与 TiCDC、BR(备份恢复)、二级索引等生态工具兼容良好,删除行为本身就是普通的 SQL DML,可被这些组件识别与同步。

设计文档同时分析了其他备选方案的短板:

候选方案主要问题
复用 TiKV RawKV 上的 TTL不具备 SQL 感知;无法用任意列(含生成列)做 TTL 列;无法支撑未来外键;TTL 是 KV 级别的,不能按表开关;与 CDC、备份、二级索引不兼容
将 TTL 配置下推给 TiKV 执行存在未解决的隔离性问题(如可能破坏快照隔离约束)
CockroachDB 行级 TTL理念相似,但 CockroachDB 用隐藏列存储"到期时间戳"而非直接比较"创建时间列",因此ALTERTTL 选项时会引发存量数据改写,性能受表大小影响

TiDB 选择"直接用创建时间列 + INTERVAL 表达式"的方式,ALTER表级 TTL 配置只改元数据、不触碰数据行,这是它在实现层面的一个优势。

三、TTL 表 DDL 语法完整指南

设计文档给出的语法分为建表、改表、移除三类,下面结合仓库实现逐一说明。

3.1 创建 TTL 表

最基本的 TTL 表把created_at声明为 TTL 时间列,数据在写入 3 个月后过期:

CREATE TABLE t1 ( id int PRIMARY KEY, created_at TIMESTAMP ) TTL = `created_at` + INTERVAL 3 MONTH;

其中TTL = <time_column> + INTERVAL <n> <unit>即"到期时间 = 时间列值 + 保留时长"。保留时长由数值与时间单位组成,时间单位在语法层面支持到ast.TimeUnitType(如MONTHDAYHOUR等),可参考源码中对IntervalTimeUnit的建模(pkg/meta/model/table.go 中的TTLInfo结构体将IntervalExprStrIntervalTimeUnit分开存储)。

3.2 用 TTL_ENABLE 开关后台任务

默认建表后 TTL 作业即为开启状态,若只想声明 TTL 语义而暂不执行删除,可显式关闭:

CREATE TABLE t1 ( id int PRIMARY KEY, created_at TIMESTAMP ) TTL = `created_at` + INTERVAL 3 MONTH TTL_ENABLE = 'OFF';

TTL_ENABLE缺省时为ON。在 DDL 解析与落库阶段,getTTLInfoInOptions(见 pkg/ddl/ttl.go)会把TableOptionTTLTableOptionTTLEnableTableOptionTTLJobInterval三类选项聚合成model.TTLInfoEnable默认置true,若同时出现TTL_ENABLE则以显式值为准(pkg/ddl/create_table.go、pkg/ddl/executor.go 中对ast.TableOptionTTL等选项的分支处理可印证)。

3.3 兼容 MySQL 的注释式语法

为使语法形态与 MySQL 生态兼容,TTL 选项同样支持放在特殊注释块中:

CREATE TABLE t1 ( id int PRIMARY KEY, created_at TIMESTAMP ) /*T![ttl] TTL = `created_at` + INTERVAL 3 MONTH TTL_ENABLE = 'OFF'*/;

MySQL 及不支持 TTL 的下游工具会将其整体视为普通注释而忽略,TiDB 则解析其中的 TTL 配置。

3.4 ALTER 为已有表添加或更新 TTL

对一张已存在(非 TTL)表,可直接通过ALTER TABLE追加 TTL 属性:

ALTER TABLE t1 TTL = `created_at` + INTERVAL 3 MONTH;

更新已有 TTL 配置时,ALTER会以最小覆盖原则合并:只修改本次给出的选项,其余选项沿用旧值。这一行为在 DDL 执行逻辑onTTLInfoChange中有明确实现——当只改TTL_ENABLE或只改TTL_JOB_INTERVAL而对应表尚未有 TTL 配置时,会报ErrSetTTLOptionForNonTTLTable(参见 pkg/ddl/ttl.go);更新完成后,该表正在运行的后台作业会按最新配置停止或重启。

文档示例中,TTL_JOB_INTERVAL用于自定义作业调度周期,例如把表t1的清理作业周期调整为 1 天:

ALTER TABLE t1 TTL_JOB_INTERVAL='1d';

调度周期既支持设计文档里的ALTER表级设置,也支持在CREATE TABLE时一并声明。注意默认值的版本演进:设计文档写作时默认周期为 1 小时,而当前仓库中表级默认值已演进为24h(见 pkg/meta/model/table.go 中DefaultTTLJobInterval = "24h");同时保留了OldDefaultTTLJobInterval = "1h"作为 8.5 及之前版本的兼容值——当老版本建的表JobInterval字段为空时,GetJobInterval()会回退返回1h,保证从 v6.5 一路升级过来的集群行为一致。

3.5 移除 TTL:REMOVE TTL

与 MySQL 的ALTER TABLE ... REMOVE PARTITIONING思路类似,去掉表的 TTL 属性只需:

ALTER TABLE t1 REMOVE TTL;

对应 DDL 内部流程为onTTLInfoRemove(pkg/ddl/ttl.go):置空tblInfo.TTLInfo并落库,作业随后被终止。

3.6 生成列作为 TTL 时间列

设计文档指出,TTL 表支持把生成列(Generated Column)作为时间列,例如把 JSON/VARCHAR 中的时间字段转换为DATETIME后再作为 TTL 列。但文档明确提示当前存在性能退化:

  1. 生成列的表达式条件无法下推到 TiKV,过滤工作只能在 TiDB 侧完成,扫描阶段网络与 CPU 开销更高;
  2. 多数日期函数同样不支持下推。

要从根本上解决,需要"生成列表达式支持下推 TiKV"与"常用日期函数支持下推 TiKV"两项能力(详见本文"已知问题"一节)。

3.7 使用约束与合法性校验

TTL 表并非可以随意声明,仓库 pkg/ddl/ttl.go 的checkTTLInfoValid汇总了全部校验规则,可归纳为:

  • 外键约束:被其他表通过外键引用的表不能加 TTL(父表被引用时,删除父表行可能违反子表外键约束),对应报错ErrUnsupportedTTLReferencedByFK。设计文档给出的描述与该实现一致。
  • 时间列类型:TTL 时间列必须是时间类型(DATE/DATETIME/TIMESTAMP),否则报ErrUnsupportedColumnInTTLConfig
  • 列存在性:TTL 配置指向的列必须真实存在,否则报ErrBadField
  • 临时表:临时表不允许设置 TTL(ErrTempTableNotAllowedWithTTL)。
  • 聚簇主键类型:若表使用聚簇主键且主键含FLOAT/DOUBLE列,不允许创建 TTL(ErrUnsupportedPrimaryKeyTypeWithTTL)。原因是删除过期行走的是 SQLDELETE ... WHERE PK IN (...),浮点主键在比较时存在精度损失风险。
  • 间隔表达式合法性:创建/修改时会用cache.EvalExpireTime预计算,校验INTERVAL表达式可被正确求值。
  • 列保护:TTL 时间列不能被DROP COLUMN删除(ErrTTLColumnCannotDrop)。

四、表级 TTL 配置在元数据中的存储形态

TTL 属性最终落在每张表的元数据TableInfo.TTLInfo上,其结构定义在 pkg/meta/model/table.go:

字段含义
ColumnNameTTL 时间列名
IntervalExprStr间隔表达式的文本(如INTERVAL 3 MONTH3 MONTH部分经过 RESTORE 后的字符串)
IntervalTimeUnit间隔时间单位(内部为ast.TimeUnitType,以int存储避免循环依赖)
Enable该表 TTL 作业是否启用(对应TTL_ENABLE
JobInterval两次 TTL 作业之间的间隔(字符串形式,如24h

JobInterval的解析统一走(*TTLInfo).GetJobInterval(),其中注入了 failpointoverwrite-ttl-job-interval以便测试覆盖。这也说明"多久跑一次作业"在最终实现中是每表各自可配置的(设计文档中提及的全局tidb_ttl_job_run_interval变量,在落地演进中被表级TTL_JOB_INTERVAL+ 全局作业开关等变量取代,使用时请以当前版本的 系统变量清单 为准)。

五、后台作业调度与任务管理架构

5.1 作业模型概述

设计文档给出的作业模型是:

  • 为每个 TTL 表在"需要时"调度一个作业(Job),作业与物理表一一对应;
  • 尽量把不同表的作业分散到不同 TiDB 节点执行,降低对单个节点的影响;
  • 一个分区表会被视为多个物理表:每个分区对应一条状态记录,因而同一时刻可能有多个作业在不同 TiDB 节点上并行运行。

在 pkg/ttl/ttlworker/config.go 中可以看到调度器的若干关键周期常量,例如作业管理器主循环 10s、任务管理器循环 1 分钟、任务心跳 1 分钟、任务领取轮询 5 秒、作业超时上限 6 小时等;这些常量均可通过 failpoint 注入改写以便集成测试,从侧面印证调度循环的周期性特征。

5.2 状态记录表 mysql.tidb_ttl_table_status

每个 TTL 物理表的状态记录在系统表mysql.tidb_ttl_table_status中。设计文档给出的建表结构完整继承了表中全部字段:

CREATE TABLE `tidb_ttl_table_status` ( `table_id` bigint(64) PRIMARY KEY, `parent_table_id` bigint(64), `table_statistics` TEXT DEFAULT NULL, `last_job_id` varchar(64) DEFAULT NULL, `last_job_start_time` timestamp NULL DEFAULT NULL, `last_job_finish_time` timestamp NULL DEFAULT NULL, `last_job_ttl_expire` timestamp NULL DEFAULT NULL, `last_job_summary` text DEFAULT NULL, `current_job_id` varchar(64) DEFAULT NULL, `current_job_owner_id` varchar(64) DEFAULT NULL, `current_job_owner_addr` varchar(256) DEFAULT NULL, `current_job_owner_hb_time` timestamp, `current_job_start_time` timestamp NULL DEFAULT NULL, `current_job_ttl_expire` timestamp NULL DEFAULT NULL, `current_job_state` text DEFAULT NULL, `current_job_status` varchar(64) DEFAULT NULL, `current_job_status_update_time` timestamp NULL DEFAULT NULL );

该表在集群 bootstrap 阶段随其他系统表一同创建,见 pkg/session/bootstrap.go(tidb_ttl_table_status与后文tidb_ttl_task均在其中注册)。字段语义按设计文档可归纳为:

  • table_id:TTL 表(物理表)ID;分区表场景下是每个分区的物理 ID。
  • parent_table_id:若当前行为某个分区,则为其父表 ID,否则等于table_id
  • table_statistics:表的统计信息。
  • last_job_*前缀:最近一次成功执行作业的信息,包括作业 ID、开始/完成时间、本次作业使用的过期时间(last_job_ttl_expire)以及作业摘要(last_job_summary)。
  • current_job_*前缀:当前尚未结束作业的信息,除 ID、开始时间、过期时间外,还包括:
    • current_job_owner_id/current_job_owner_addr/current_job_owner_hb_time:作业宿主(某个 TiDB 节点)的 ID、地址与心跳时间。宿主周期性刷新心跳;若心跳长期不更新,说明原宿主已下线,作业会被其他节点接管(fail over)。
    • current_job_state:作业内部状态,用于失败后的状态恢复。
    • current_job_status:作业生命周期状态。
    • current_job_status_update_time:状态最近更新时间。

设计文档给出的current_job_status枚举为waiting / running / cancelling / cancelled / error;在 pkg/ttl/cache/ttlstatus.go 的实现中,该枚举进一步演进为waiting / running / cancelling / cancelled / timeout / finished六态,实际使用时以上述源码定义为准。current_job_id不只代表正在运行的作业,失败或用户取消但尚未清理的作业同样保留其中,方便排查。

5.3 取消运行中的作业

需要终止某个作业时,执行管理命令:

ADMIN CANCEL TTL JOB 123456789

作业状态会先变为cancelling,随后最终更新为cancelled

5.4 分布式任务表 mysql.tidb_ttl_task

为了让集群资源得到最大化利用,设计文档提出把单个作业拆成多个扫描任务并分发到所有 TiDB 节点执行:

  • 每个新作业创建时,向系统表mysql.tidb_ttl_task写入若干行,每行代表一个扫描任务;
  • 每个 TiDB 节点周期性扫描该表,发现新任务后把任务 owner 置为自己并执行。

在 pkg/ttl/ttlworker/task_manager.go 中可以看到对应的 SQL 模板族:UPDATE mysql.tidb_ttl_task设置任务 owner、标记任务完成、刷新心跳、放弃 owner(resign),以及SELECT count(1) ... WHERE status = 'running'统计运行中任务数,完整佐证了"认领—执行—心跳—收尾"的分布式任务生命周期。任务行还记录了扫描范围(scan_range_start/scan_range_end)、过期时间与创建时间,见 pkg/ttl/cache/task.go。集成测试 pkg/ttl/ttlworker/task_manager_integration_test.go 中模拟多 TiDB 节点(task-manager-1/task-manager-2)竞争领取任务并验证"running → finished"状态迁移,是理解该机制最直接的样例。

六、作业执行细节:扫描与删除

一个运行中的 TTL 作业包含两类任务:扫描任务(Scan Tasks)负责从表中过滤出过期行,删除任务(Delete Tasks)负责批量删除。全部过期行删完后作业结束。单个 TiDB 节点上运行多个 worker 并发消费这些任务。

6.1 扫描任务:按主键范围分批翻页

作业开始时,先把表按主键切分成 N(N ≥ 1)个范围,每个范围交给一个扫描任务执行范围扫描。设计文档给出的伪代码如下:

func doScanTask(tbl, range, expire, ch) { var lastRow for { selectSQL := buildSelect(tbl, range lastRow, expire, LIMIT) rows := execute(selectSQL) ch <- deleteTask{tbl, expire, rows} if len(rows) < LIMIT { break } lastRow := rows[len(rows)-1] } }

首次查询形如:

SELECT LOW_PRIORITY id FROM t1 WHERE create_time < '2022-01-01 00:00:00' AND id >= 12345 AND id < 45678 ORDER BY id ASC LIMIT 500;

其中'2022-01-01 00:00:00'是作业启动时刻计算出的过期时间,[12345, 45678)是当前扫描任务负责的键范围,LIMIT 500为单次最多返回行数,可用系统变量tidb_ttl_scan_batch_size调整。由于一次查询通常取不完所有过期行,当返回行数达到上限时,就把上次读到的最后一行主键(如23456)作为下一次查询的起点继续翻页:

SELECT LOW_PRIORITY id FROM t1 WHERE create_time < '2022-01-01 00:00:00' AND id >= 23456 AND id < 45678 ORDER BY id ASC LIMIT 500;

直到某次返回行数不足LIMIT即代表该范围读尽。需要注意的关键点:

  • 扫描很重:它本质上是全表范围扫描,因此对大表而言,建议把作业周期(如TTL_JOB_INTERVAL/ 作业调度窗口)调大,摊薄单日资源开销。
  • 无主键/非聚簇主键表:使用隐藏列_tidb_rowid作为行号参与范围切分与翻页。
  • 生成列时间列:条件无法下推时,过滤在 TiDB 侧完成,需要更多网络流量与 CPU。

在实现层面,pkg/ttl/ttlworker/scan.go 的doScanWithSession除了逐页翻页(读取当前vardef.TTLScanBatchSize作为limit),还做了两类加固,比设计文档更进一步:

  1. 安全过期时间校验:任务执行前会把"作业计算出的过期时间"与"按当前最新配置重新计算的安全过期时间"对比。因为作业提交后、任务真正执行前,用户可能已把保留时长从 2 天改成 1 天,若仍按旧的过期时间删除就可能误删本不该删的行,因此校验不通过时任务会直接报错中止(对应代码注释中的完整时序说明)。
  2. 失败重试:单条扫描 SQL 执行失败时按scanTaskExecuteSQLMaxRetry = 5次、间隔 2 秒的重试策略处理(pkg/ttl/ttlworker/config.go 与 pkg/ttl/ttlworker/scan.go)。

6.2 删除任务:分批多行 DELETE

删除 worker 从 channel 中消费扫描阶段投递的任务,再把行拆成若干批次。设计文档伪代码如下:

func doDelTask(ch) { for _, task := range ch { batches := splitRowsToDeleteBatches(task.rows) for _, batch := range batches { deleteBatch(task.tbl, task.batch, task.expire) } } }

批次大小由系统变量tidb_ttl_delete_batch_size控制,每个批次用一条多行DELETE完成:

DELETE LOW_PRIORITY FROM t WHERE id in (1, 2, 3, ...) AND create_time < '2022-01-01 00:00:00';

这里刻意保留create_time < '...'条件,是为了避免误删:某行在扫描时已过期,但在真正执行删除前可能被业务更新为未过期值,此时id IN (...)即使命中,也会因时间条件不满足而被跳过。

关于性能,设计文档指出:若 TTL 表没有二级索引,传入删除的行大多落在同一 Region,删除操作多数情况下可以用1PC(一阶段提交)提交,事务开销显著降低。因此对大表而言,不建二级索引往往反而能获得更好的 TTL 清理性能。

6.3 SQL 构造器的实现佐证

扫描与删除 SQL 并非手工字符串拼接,而是由 pkg/ttl/sqlbuilder/sql.go 的SQLBuilder状态机式生成。可以看到:

  • 查询以SELECT LOW_PRIORITY SQL_NO_CACHE开头,删除以DELETE LOW_PRIORITY FROM开头——LOW_PRIORITY用于降低 TTL 后台任务对在线查询的影响,与设计文档示例一致;
  • 建表 SQL 支持分区表,构造时会追加PARTITION(<name>)限定到具体分区;
  • 构造器内部强制要求写入time_col < expire的过期条件(WriteCommonCondition),否则在生成最终 SQL 时报"expire condition not write"错误——从代码层面保证"任何删除都携带过期条件",杜绝无差别删除。

七、时区考量

TTL 时间列支持三种字段类型:DateDateTimeTimeStamp。其中Date/DateTime不是绝对时间点,必须结合时区才能确定精确的到期时刻,设计文档列出了两种时区选择方案及其隐患:

  1. 使用系统时区(全局变量system_time_zone,集群 bootstrap 时确定且之后不变):行会在固定的绝对时刻被删除,不会因用户改时区而漂移;但如果集群把会话/全局time_zone设置为与SYSTEM不同(例如system_time_zone+08:00time_zoneUTC),在作业周期小于 8 小时时,记录会被"立即视为过期"而误删。
  2. 使用集群时区(变量time_zone):在time_zone不变化时表现良好;但一旦用户修改time_zone,TTL 作业必须及时感知新时区,否则"某行是否过期"在不知道历史时区的情况下无法判定,可能删除非预期行。

这一节说明:TTL 表在使用DATE/DATETIME作为时间列时,部署时应保证集群时区设置长期稳定,并清楚其相对时间的语义TIMESTAMP存储的是绝对时间点,不存在此类歧义。

八、系统变量全览

设计文档规划了 8 个全局系统变量。对照仓库中的变量注册表 pkg/sessionctx/vardef/tidb_vars.go 与 pkg/sessionctx/variable/sysvar.go,实际落地的变量清单与参数如下(个别默认值在实现中已与设计稿不同,已按仓库当前定义修正):

系统变量作用作用域取值范围默认值
tidb_ttl_job_enable全局总开关;OFF时停止调度新作业并取消正在运行的作业GlobalON/OFFON
tidb_ttl_job_schedule_window_start_timeTTL 作业调度时间窗口的开始时刻Global时间值00:00 +0000
tidb_ttl_job_schedule_window_end_timeTTL 作业调度时间窗口的结束时刻Global时间值23:59 +0000
tidb_ttl_scan_worker_count每个 TiDB 节点的扫描 worker 数Global[1, 256]4
tidb_ttl_scan_batch_size扫描任务中每条 SELECT 的 LIMIT 值Global[1, 10240]500
tidb_ttl_delete_worker_count每个 TiDB 节点的删除 worker 数Global[1, 256]4
tidb_ttl_delete_batch_size每条 DELETE 的批次大小Global[1, 10240]100
tidb_ttl_delete_rate_limit每个 TiDB 节点的删除速率上限,0 表示不限速Global[0, MaxInt64]0
tidb_ttl_running_tasks集群中允许同时运行的任务数上限;支持自动值Global自动值或 [1, 最大并发]-1(由 TiDB 自动决定,早期设计稿为 0)

以上变量的当前默认值可以从vardef包中的DefTiDBTTL*常量直接核对(如DefTiDBTTLScanBatchSize = 500DefTiDBTTLDeleteBatchSize = 100DefTiDBTTLScanWorkerCount = 4等)。其中tidb_ttl_running_tasks在设计稿中的含义是"最大并行任务数,-1表示由 TiDB 自动决定";变量类型注册为TypeIntAllowAutoValue: true,说明允许赋自动值以让系统按集群规模动态确定。

使用方式示例:

SET @@global.tidb_ttl_job_enable = 'ON'; SET @@global.tidb_ttl_scan_worker_count = 8; SET @@global.tidb_ttl_delete_batch_size = 200; SET @@global.tidb_ttl_job_schedule_window_start_time = '01:00 +0000'; SET @@global.tidb_ttl_job_schedule_window_end_time = '05:00 +0000';

九、可观测性:TTL 监控指标

设计文档规划了 6 组 Prometheus 指标,用于观察 TTL 作业健康度与开销;相关定义可在 pkg/ttl/metrics/metrics.go 及全局 metrics 定义中找到对应实现:

指标类型Labels含义
ttl_queriesCountersql_type,resultTTL 作业执行的查询总数
ttl_processed_expired_rowsCountersql_typeTTL 作业处理的过期行总数
ttl_query_durationHistogramsql_type,resultTTL 查询耗时分布
ttl_job_statusGauge(实现中按作业状态打标)当前 TTL 作业所处状态;处于该状态时为 1,否则为 0
ttl_phase_timeCountertype,phase各 worker 在不同执行阶段(如 idle、begin/commit txn、query、wait retry、dispatch、check TTL、wait token 等,见 pkg/ttl/metrics/metrics.go 中的 Phase 常量)的耗时
ttl_insert_rowsCounter写入 TTL 表的总行数

设计文档对ttl_queries等指标的sql_type取值为select/deleteresult取值为ok/error。在实现中扫描、删除成功与失败的行数分别通过不同 Counter 记录(如ScannedExpiredRowsDeleteSuccessExpiredRowsDeleteErrorExpiredRows),运行中/取消中的作业数量由RunningJobsCntCancellingJobsCnt等 Gauge 体现。这些指标配合 Grafana 即可监控"TTL 是否在按窗口执行、单位时间清理了多少行、后台任务对集群压力有多大"。

十、已知问题与性能影响

设计文档明确列出的已知问题是:

生成列条件无法下推到 TiKV。若表使用生成列作为 TTL 时间列,过滤会在 TiDB 侧完成,带来额外网络流量并使查询变慢。

该限制在文档写作时存在,也属于"未来工作"中性能优化部分的头号议题(下推生成列表达式、或直接用生成列定义构造过滤条件)。因此当前使用 TTL 时,若对清理性能敏感,应优先选择真实时间列而非生成列。另外如前文所述,无二级索引的表由于删除通常可走 1PC、且无索引写入热点,往往表现更好。

十一、未来演进方向

11.1 性能优化

设计文档规划的优化方向包括:

  • 若 TTL 表存在 TiFlash 副本,扫描改走 TiFlash 以卸载 TiKV 压力;
  • 若存在以 TTL 时间列为前缀的索引,用索引定点查询过期行,替代全表范围扫描,缩短扫描耗时;
  • 对无二级索引的表,利用统计信息缓存(例如作业结束后缓存每个 Region 最旧行的创建时间),下次作业开始时跳过"无更新且未过期"的 Region;
  • 对无二级索引的表,把扫描与删除下推到 TiKV 侧执行,避免 TiDB 与 TiKV 之间的数据搬运——类似 GCWorker,引入新的 Coprocessor 命令TTLGC,由 TiKV 以非事务方式直接清理过期行;
  • 结合全局资源控制框架(设计时仍在进行中),最大化集群资源利用率。

11.2 更多功能支持

  • 支持 TTL 表作为被ON DELETE CASCADE子表引用的父表,实现级联清理;
  • 支持将生成列条件下推 TiKV,或用生成列定义直接构造过滤条件;
  • 根据集群当前负载动态调整运行期参数;
  • 为云环境增加内置 ALTER 支持。

十二、测试方案与仓库验证

设计文档给出了功能与性能两方面的测试计划:

功能测试(Functional Test)

  • DDL 操作:创建带 TTL 的表、对非 TTL 表执行 ALTER 增加 TTL、对 TTL 表 ALTER 更新 TTL 选项、移除表的 TTL 选项;
  • TTL 表能调度后台作业并删除过期行;
  • TTL_ENABLE或全局@@global.tidb_ttl_job_enableOFF时表不调度作业;
  • 作业只在配置的时间窗口内被调度。

性能测试(Performance Test)

  • 构造 1000 万行表、其中 10% 为过期行,测试清空过期数据所需时间;
  • 同时运行基准测试与 TTL 作业,采集基准的 QPS/延迟并与未运行 TTL 时对比,评估对在线负载的影响。

这些计划在当前仓库中均有对应的工程化验证:DDL 侧有 pkg/ddl/ttl_test.go(校验默认间隔、TTL_ENABLE选项的解析与持久化)、pkg/ddl/db_table_test.go;作业与任务侧有 pkg/ttl/ttlworker/job_manager_test.go、pkg/ttl/ttlworker/scan_test.go、pkg/ttl/ttlworker/del_test.go,以及多节点场景的 pkg/ttl/ttlworker/integrationtest 集成测试目录;TTL 表状态读写与字段抽取逻辑见 pkg/ttl/cache/ttlstatus_test.go。读者若想深入理解某个机制,直接阅读对应测试是最高效的入口。

结语

TiDB TTL 表把"过期数据自动清理"做成了表级声明式能力:通过TTL = 时间列 + INTERVAL语法定义保留策略,由 TiDB 后台以 SQL 层作业(Job 拆分为 Scan/Delete 任务)在集群内分布执行。相比手工脚本,它具备统一元数据管理、天然兼容 TiCDC/BR/二级索引、作业状态可观测、可全局开关与限速等工程化优势;相比存储层 TTL 方案,则避免了 KV 级语义带来的隔离性、表感知与外键兼容问题。理解其语法、调度状态机、任务分发与各项系统变量,是在生产环境中正确、高效使用 TTL 的关键前提。

【免费下载链接】tidbTiDB is built for agentic workloads that grow unpredictably, with ACID guarantees and native support for transactions, analytics, and vector search. No data silos. No noisy neighbors. No infrastructure ceiling.项目地址: https://gitcode.com/GitHub_Trending/ti/tidb

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 21:19:24

小波变换在语音降噪中的MATLAB实现与优化

1. 语音降噪技术背景与应用场景在语音通信、语音识别和音频处理领域&#xff0c;背景噪声一直是影响信号质量的关键问题。无论是电话会议中的环境杂音&#xff0c;还是录音设备采集的电流声&#xff0c;都会显著降低语音清晰度。传统降噪方法如傅里叶变换滤波存在时频分辨率固定…

作者头像 李华
网站建设 2026/9/10 21:17:32

Qt与C++图形开发实战:从基础到高级应用

1. C与Qt图形开发概述 在桌面应用开发领域&#xff0c;C与Qt的组合堪称黄金搭档。作为一名长期使用这套技术栈的开发者&#xff0c;我见证过太多项目从零开始到最终交付的全过程。Qt框架提供的跨平台能力和丰富的图形组件&#xff0c;配合C的高性能特性&#xff0c;能够打造出既…

作者头像 李华
网站建设 2026/9/10 21:17:06

UG NX CAM二次开发:非切削移动与区域间转移优化

1. UG NX CAM二次开发中的非切削移动概述在UG NX CAM模块的二次开发中&#xff0c;非切削移动&#xff08;Non-Cutting Moves&#xff09;的设置是刀具路径规划的关键环节。作为拥有十年数控编程经验的工程师&#xff0c;我深刻理解合理设置非切削移动对于加工效率和安全性的重…

作者头像 李华
网站建设 2026/9/10 21:16:55

嵌入式Linux进程开发与优化实战指南

1. 嵌入式Linux进程开发概述 在嵌入式Linux系统中&#xff0c;进程管理是应用开发的核心基础。与桌面系统不同&#xff0c;嵌入式环境对资源占用、稳定性和实时性有着更严格的要求。我曾参与过多个工业级嵌入式项目&#xff0c;深刻体会到合理的进程设计能显著提升系统可靠性。…

作者头像 李华