MongoDB 的 TTL 索引是什么?适合哪些场景?
简化版
TTL 索引用来让 MongoDB 自动删除过期文档,常用于会话、验证码、临时 token、日志和短期事件数据。它依赖日期字段和后台清理线程,删除不是毫秒级准时触发,不能用于强实时过期业务。
详细版
TTL 索引通常建在单个日期字段上,并通过 expireAfterSeconds 指定保留时间:
db.sessions.createIndex(
{ expiresAt: 1 },
{ expireAfterSeconds: 0 }
)
当 expiresAt 早于当前时间时,文档会被后台任务删除。面试要强调 3 点:TTL 删除是异步的;字段类型要是日期或日期数组;适合清理冷数据,不适合作为严格定时任务或支付超时这种强一致触发器。
完整版教学
一、TTL 索引解决自动清理问题
很多业务数据天然有生命周期:登录 session 30 分钟过期,验证码 5 分钟有效,接口幂等 token 24 小时保留,埋点日志只保留 7 天。
如果完全靠应用定时任务删除,容易出现任务漏跑、批量删除压力大、多个服务重复清理等问题。TTL 索引把“到期删除”交给 MongoDB 后台机制处理。
db.login_codes.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 300 }
)
这表示文档的 createdAt 超过 300 秒后,会被后台清理。
记忆钩子:TTL 索引是数据库内置的“保质期标签”,适合自动清理,不适合准点触发。
二、TTL 有两种常见建模方式
第一种是“创建时间 + 保留秒数”:
db.events.createIndex(
{ createdAt: 1 },
{ expireAfterSeconds: 7 * 24 * 3600 }
)
第二种是“到期时间 + 0 秒”:
db.sessions.createIndex(
{ expiresAt: 1 },
{ expireAfterSeconds: 0 }
)
| 方式 | 字段含义 | 适合场景 |
|---|---|---|
createdAt + N | 从创建时间开始保留 N 秒 | 日志、埋点、验证码 |
expiresAt + 0 | 到具体时间点过期 | session、token、优惠券锁定 |
第二种更灵活,因为每个文档可以有不同过期时间。
三、TTL 删除不是准实时
TTL 索引由后台任务周期性扫描并删除过期文档,因此过期文档可能会多存活一小段时间。
这点在面试里很关键。TTL 适合“最终会被清理”的场景,不适合“到点必须立即执行动作”的场景。
例如支付订单 15 分钟未支付要关闭,不能只依赖 TTL 删除订单。更合理的是状态机加定时扫描、延迟消息或任务调度,TTL 最多用于清理临时锁或过期辅助数据。
验证码过期 -> TTL 可以
支付订单关闭 -> 业务任务处理状态,TTL 不做主流程
审计日志保留 180 天 -> TTL 可以
四、TTL 字段类型和索引限制要记住
TTL 索引通常要求字段是日期类型,或者包含日期值的数组。字段不存在或类型不对,文档不会按预期删除。
// 推荐
{ createdAt: ISODate("2026-07-29T10:00:00Z") }
// 风险:字符串不是日期类型
{ createdAt: "2026-07-29 10:00:00" }
TTL 索引一般是单字段索引,不是拿来替代复合查询索引的。不要指望一个 TTL 索引同时解决过期删除和复杂查询排序。
如果业务既要按 userId 查询,又要过期删除,通常需要分别设计查询索引和 TTL 索引。
五、大批量过期会带来删除压力
如果很多文档在同一时间过期,后台删除会造成 I/O 和复制压力。比如每天 0 点让 1000 万条文档同时过期,会产生大量删除操作。
更平滑的方式是让过期时间分散,或者按时间分桶建集合,老集合整体下线。
| 数据量 | 过期分布 | 建议 |
|---|---|---|
| 小 | 均匀 | TTL 索引即可 |
| 大 | 集中 | 分散过期时间或削峰 |
| 极大 | 按天保留 | 考虑按日期集合或归档 |
TTL 删除本身也会进入复制流程,所以 Secondary 同样要应用删除操作。
六、TTL 与业务状态要分层
TTL 删除的是文档,不是业务事件处理器。如果删除本身会触发后续动作,比如退款、通知、释放库存,那就不能只靠 TTL。
更可靠的设计是:
业务状态表:记录订单、锁定、超时状态
调度系统:到期扫描或延迟触发
TTL 数据:清理临时 token、验证码、幂等记录
也就是说,TTL 适合作为数据生命周期管理工具,不适合作为核心业务流程编排工具。
七、常见误区与追问
- 误区:TTL 到期会立刻删除。 TTL 删除由后台任务异步执行,可能存在延迟。
- 误区:TTL 可以替代订单超时任务。 订单关闭涉及状态流转和副作用,应该由业务任务保证,TTL 只适合清理辅助数据。
- 误区:日期字符串也能正常 TTL。 TTL 字段应使用日期类型,字符串通常不会按预期过期。
- 追问:
expireAfterSeconds: 0表示什么? 表示直接使用字段里的到期时间,字段时间早于当前时间就可被清理。 - 追问:大批量 TTL 删除有什么风险? 会带来磁盘、复制和锁竞争压力,可能造成复制延迟。
- 追问:TTL 索引能不能同时优化查询? 不建议依赖它解决复杂查询,查询索引和生命周期索引应分别设计。
八、加强记忆
TTL 索引要记住“日期字段、后台删除、最终清理”。它用 expireAfterSeconds 给文档生命周期兜底,适合验证码、session、临时 token、日志等短生命周期数据;但删除不是准实时,字段类型必须正确,大批量集中删除会有压力。业务状态到期要靠任务或消息驱动,TTL 负责清理数据,不负责推动复杂业务流程。