摘要
数据库 TTL(Time to Live)是一种自动清理过期数据的功能,适用于日志、监控指标、时序数据等有时间生命周期的场景。本文解析 TiDB TTL 的工作原理、配置方法、性能影响,以及与传统手动清理方案的对比,帮助架构师和 DBA 选择最适合的数据过期策略。
本文适合谁: 需要管理大规模时间序列数据的 DBA、运维工程师、后端架构师,以及正在评估 TiDB 数据生命周期管理方案的技术决策者。
一、TTL 概念与应用场景
TTL(Time to Live,数据生存时间)指数据库自动根据数据的时间属性删除过期记录的机制。数据写入时记录创建时间或过期时间,后台任务定期扫描并清理超过生命周期的数据。
典型应用场景
| 场景 | 数据特点 | TTL 建议 |
|---|---|---|
| 应用日志 | 持续写入,查询集中在近期 | 保留 7-30 天 |
| 监控指标 | 高频写入,趋势分析为主 | 保留 30-90 天 |
| 会话/Token | 写入后逐条过期 | 保留数小时到数天 |
| 审计记录 | 合规要求,保留期限固定 | 保留 90-365 天 |
| 设备时序数据 | 物联网高频采集 | 保留 7-180 天 |
| 缓存表 | 临时计算结果 | 保留数小时 |
二、TiDB TTL 功能特性
2.1 工作原理
TiDB 在 v6.6+ 版本引入 TTL 功能。在表上定义 TTL 属性后,后台线程按配置的间隔自动扫描过期数据并执行删除。
写入数据(包含时间列)
│
▼
TiDB 存储引擎
(Raft + 多副本)
│
▼
TTL 后台扫描线程(默认每 1 小时触发)
│
├── 发现过期数据
│ │
│ ▼
│ 执行 DELETE(标记删除)
│ │
│ ▼
│ 后台 GC 回收物理空间
│
└── 未过期数据跳过
2.2 创建 TTL 表
CREATE TABLE device_metrics (
id BIGINT AUTO_RANDOM PRIMARY KEY,
device_id VARCHAR(64) NOT NULL,
metric_name VARCHAR(128) NOT NULL,
value DOUBLE,
recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- 定义 TTL 属性:recorded_at 列,数据保留 7 天
_tidb_ttl_expire_at TIMESTAMP GENERATED ALWAYS AS (
recorded_at + INTERVAL 7 DAY
) STORED,
INDEX idx_recorded_at (recorded_at)
) TTL = `_tidb_ttl_expire_at`
TTL_JOB_INTERVAL = '1h';
关键参数说明:
| 参数 | 说明 | 默认值 |
|---|---|---|
| `TTL` | TTL 表达式,指定过期时间列 | 无(必须指定) |
| `TTL_JOB_INTERVAL` | 后台扫描间隔 | 1 小时 |
| `TTL_DELETE_BATCH_SIZE` | 每批删除行数 | 5000 |
2.3 为已有表启用 TTL
-- 方法一:通过 ALTER TABLE 添加 TTL 属性
ALTER TABLE app_logs
SET TTL = `created_at` + INTERVAL 30 DAY,
SET TTL_JOB_INTERVAL = '1h';
-- 方法二:添加 TTL 过期时间列(更灵活)
ALTER TABLE app_logs
ADD COLUMN _ttl_expire_at TIMESTAMP GENERATED ALWAYS AS (
created_at + INTERVAL 30 DAY
) STORED,
SET TTL = `_ttl_expire_at`,
SET TTL_JOB_INTERVAL = '1h';
2.4 管理 TTL 任务
-- 查看当前所有 TTL 表
SELECT TABLE_NAME, TTL_EXPR, TTL_JOB_INTERVAL
FROM information_schema.tidb_ttl_table_info;
-- 查看最近一次 TTL 任务执行状态
SELECT * FROM information_schema.tidb_ttl_job_history
ORDER BY created_time DESC LIMIT 10;
-- 手动触发一次 TTL 清理(适用于紧急清理场景)
SET @@tidb_ttl_job_schedule_interval = '0s';
-- 等待任务执行后恢复
SET @@tidb_ttl_job_schedule_interval = DEFAULT;
三、TTL vs 手动清理 vs 外部调度对比
| 维度 | TiDB TTL | 手动 DELETE | 外部调度(cron + 程序) |
|---|---|---|---|
| 实现复杂度 | 低(SQL DDL 配置) | 低(单条 SQL) | 高(程序 + 调度 + 错误处理) |
| 自动化程度 | 全自动 | 手动或脚本触发 | 半自动 |
| 性能影响 | 可控(分批删除) | 大量 DELETE 易造成锁争用 | 取决于实现质量 |
| 空间回收 | 自动 GC 回收 | 需等待 GC | 需等待 GC |
| 错误处理 | 内置重试和容错 | 需自行实现 | 需自行实现 |
| 粒度控制 | 表级别 | 灵活(条件过滤) | 灵活 |
| 跨表事务 | 不支持(单表) | 支持 | 取决于实现 |
| 运维成本 | 极低 | 中(需维护脚本) | 高 |
手动清理方案的问题
-- ❌ 危险操作:大批量 DELETE 不加 LIMIT
DELETE FROM app_logs WHERE created_at < '2024-01-01';
-- 问题:一次性锁定大量行,造成长事务、MDL 锁、复制延迟
-- ✅ 分批删除
DELETE FROM app_logs WHERE created_at < '2024-01-01' LIMIT 5000;
-- 需要循环执行直到删完,运维负担重
四、TTL 配置与最佳实践
4.1 参数调优建议
-- 数据量较大、清理压力大的表
ALTER TABLE device_metrics
SET TTL_JOB_INTERVAL = '10m', -- 缩短扫描间隔,更频繁清理
SET TTL_DELETE_BATCH_SIZE = 10000; -- 增大批次,加快清理速度
-- 数据量适中、对性能敏感的表
ALTER TABLE session_tokens
SET TTL_JOB_INTERVAL = '1h', -- 默认间隔
SET TTL_DELETE_BATCH_SIZE = 2000; -- 减小批次,降低对在线业务影响
| 表数据规模 | `TTL_JOB_INTERVAL` | `TTL_DELETE_BATCH_SIZE` | 说明 |
|---|---|---|---|
| < 100 万行 | 1h | 2000 | 默认配置即可 |
| 100 万 ~ 1000 万行 | 10m ~ 30m | 5000 ~ 10000 | 需缩短间隔避免积累 |
| > 1000 万行 | 5m ~ 10m | 10000 ~ 20000 | 大批量清理,注意监控 |
4.2 索引设计
-- TTL 清理依赖时间列索引,确保存在
CREATE INDEX idx_created_at ON app_logs (created_at);
-- 如果 TTL 使用 _ttl_expire_at 列,该列自动有索引(GENERATED STORED)
-- 但建议在原始时间列上也建立索引,方便业务查询
CREATE INDEX idx_recorded_at ON device_metrics (recorded_at);
4.3 分区表 + TTL(推荐组合)
对于超大表,结合分区和 TTL 可进一步优化:
CREATE TABLE metrics_2024 (
id BIGINT AUTO_RANDOM PRIMARY KEY,
device_id VARCHAR(64) NOT NULL,
value DOUBLE,
recorded_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
_tidb_ttl_expire_at TIMESTAMP GENERATED ALWAYS AS (
recorded_at + INTERVAL 90 DAY
) STORED
) PARTITION BY RANGE (TO_DAYS(recorded_at)) (
PARTITION p202401 VALUES LESS THAN (TO_DAYS('2024-02-01')),
PARTITION p202402 VALUES LESS THAN (TO_DAYS('2024-03-01')),
PARTITION p202403 VALUES LESS THAN (TO_DAYS('2024-04-01')),
PARTITION p202404 VALUES LESS THAN (TO_DAYS('2024-05-01')),
PARTITION pmax VALUES LESS THAN MAXVALUE
) TTL = `_tidb_ttl_expire_at`
TTL_JOB_INTERVAL = '1h';
分区表 + TTL 的优势:过期数据集中在特定分区,TTL 删除后可直接 `DROP PARTITION` 回收空间,效率更高。
4.4 监控建议
-- 监控各表 TTL 任务执行情况
SELECT
TABLE_NAME,
job_start_time,
job_finish_time,
TIMESTAMPDIFF(SECOND, job_start_time, job_finish_time) AS duration_sec,
expired_rows_deleted,
job_status
FROM information_schema.tidb_ttl_job_history
ORDER BY job_start_time DESC LIMIT 20;
建议监控项:
| 指标 | 关注点 |
|---|---|
| `expired_rows_deleted` | 每次删除行数,预估清理速率 |
| `duration_sec` | 清理耗时,超过阈值需调优 |
| `job_status` | 任务是否成功,失败需告警 |
| 表数据量变化 | 确认 TTL 正常生效,数据量不持续增长 |
五、TTL 对存储和性能的影响
5.1 空间回收
TTL 删除操作标记数据为删除状态,物理空间由 TiDB 后台 GC(Garbage Collection)机制异步回收:
| 阶段 | 时间点 | 空间释放 |
|---|---|---|
| TTL DELETE 执行 | 扫描触发后 | 逻辑删除,空间暂不释放 |
| TiDB GC 回收 | DELETE 完成后 | 默认 `tidb_gc_life_time = 10m` 后物理回收 |
TTL 删除 → 逻辑删除(数据仍在磁盘)→ 等待 GC safe point 推进
→ GC 线程物理回收空间
对于高频 TTL 清理场景,可以适当缩短 GC 生存时间(需评估对闪回查询的影响):
-- 调整 GC 生存时间(谨慎使用)
SET GLOBAL tidb_gc_life_time = '5m';
5.2 对在线业务的影响
TTL 删除操作以分批方式执行,对在线查询的影响可控,但仍需注意:
| 影响维度 | 表现 | 应对策略 |
|---|---|---|
| 写入吞吐 | DELETE 占用写入带宽 | 调整 `TTL_DELETE_BATCH_SIZE` |
| 读取延迟 | 大量删除导致 RocksDB 空间放大 | 定期执行 `ALTER TABLE ... COMPACT` |
| 磁盘 I/O | 删除和 GC 增加磁盘负载 | 将 TTL 表分布到不同节点 |
| 事务冲突 | TTL DELETE 与业务 DELETE 可能冲突 | 避免业务层手动删除 TTL 管理的数据 |
FAQ
Q1:TiDB TTL 精度是多少?数据是否可能在过期后仍存在一段时间?
TTL 按配置的 `TTL_JOB_INTERVAL` 周期扫描清理,默认 1 小时。因此过期数据最多存在约 1 小时 + GC 生存时间(默认 10 分钟)后才被物理清除。如果需要更严格的清理时效,缩短 `TTL_JOB_INTERVAL` 即可。
Q2:TTL 删除的数据能否恢复?
TTL 删除本质是执行 DELETE 操作。在 TiDB GC 回收之前(默认 10 分钟),可通过闪回查询(AS OF TIMESTAMP)恢复已删除数据。GC 回收后数据不可恢复,需依赖备份。
Q3:TTL 支持按自定义条件过期吗?比如按状态而不是时间?
TiDB TTL 原生仅支持基于时间列的过期策略。如果需要按自定义条件(如 status 字段)清理数据,建议使用外部调度程序或定期执行 DELETE 语句。可以在 TTL 过期列中使用表达式间接实现条件过期,但灵活性有限。
Q4:TTL 在 TiDB 集群扩缩容或故障恢复时是否正常工作?
是的。TTL 后台任务由 TiDB 节点调度,主节点故障时自动切换到其他节点继续执行。但需要注意的是,集群恢复初期 TTL 任务可能延迟启动,期间过期数据会暂时保留,不会丢失。
总结
TiDB TTL 为时间序列数据提供了自动化的生命周期管理能力,免去了手动编写清理脚本和维护调度任务的负担。通过合理配置扫描间隔、批次大小,并配合分区表使用,可以高效管理亿级行规模的数据过期清理,同时将对在线业务的影响降至最低。
下一步行动
- 试用 TiDB TTL:在 TiDB Cloud Serverless 免费创建集群,按本文示例创建 TTL 表并验证效果
- 获取数据生命周期管理方案:访问 TiDB 数据管理最佳实践 了解更多配置细节
- 下载 TTL 实战指南:访问 TiDB 时序数据管理白皮书 获取完整的企业级数据过期策略