ttl索引是mongodb原生支持的过期机制,它依赖日期类型字段,由后台线程每60秒扫描并删除已过期文档;确实能自动删数据,但非实时,延迟通常1–2分钟,且仅对含合法date值的文档生效。

什么是TTL索引,它真能自动删数据吗
TTL索引是MongoDB原生支持的过期机制,它依赖一个日期类型字段(Date),在后台定时扫描并删除已过期的文档。它不是“实时删除”,而是由后台线程每60秒检查一次,且只对集合中已建立TTL索引的字段生效。关键点:必须是Date类型字段,不能是字符串或时间戳数字;索引创建后,过期行为才启动;删除动作不可回滚。
- 必须确保字段值是合法的
Date对象(如new Date("2025-01-01")),写入"2025-01-01"字符串会导致该文档永远不被清理 - TTL索引只能建在单个字段上,不支持复合索引中的TTL行为
- 后台删除不保证精确到秒级,实际延迟通常在1–2分钟内
怎么用createIndex设置过期时间(秒)
用db.collection.createIndex()配合expireAfterSeconds选项。这个参数接收一个整数,表示从字段值开始计算的秒数。例如设为3600,就是1小时后过期。
db.sessions.createIndex({ "createdAt": 1 }, { expireAfterSeconds: 3600 })
- 字段名必须与文档中存储日期的字段一致(比如叫
createdAt、updatedAt或expiresAt) -
expireAfterSeconds可以是0(表示“过期时间即字段值本身”,适合存绝对过期时间),也可以是正整数(相对偏移) - 不要设为负数,会报错:
expireAfterSeconds must be a non-negative number - 如果集合已有大量数据,建索引过程可能阻塞写操作(副本集需注意主节点负载)
为什么文档没被删?常见失效原因
最常遇到的是字段类型不对或值为空。TTL索引对null、undefined、非Date值完全忽略——这些文档永远不会被清理。
- 检查字段是否真的存了
Date:运行db.collection.findOne({ createdAt: { $type: "date" } }),如果返回空,说明多数文档没存对 - 时间字段值早于当前时间+
expireAfterSeconds才会触发删除,比如现在是2024-05-20T10:00:00Z,expireAfterSeconds: 3600,那只有createdAt≤2024-05-20T09:00:00Z的文档才符合删除条件 - 副本集环境中,TTL删除只在主节点执行,但会通过oplog同步给从节点,所以延迟和主节点一致
- 如果集合启用了分片,TTL行为仍有效,但每个分片独立执行清理,无需额外配置
能不能用TTL替代业务层定时任务
可以,但有边界。TTL适合“简单过期”场景(如session、验证码、临时日志),不适合需要精确控制、附带回调或跨集合联动的逻辑。
- 它不触发任何钩子或事件,删了就删了,没法记录谁删的、为什么删
- 无法按条件删(比如“只删status为pending的过期文档”),TTL是无差别扫描整个索引范围
- 大量短生命周期文档(如每秒写入千条、5分钟过期)可能导致删除压力集中在后台线程,影响查询吞吐,此时建议配合定期
deleteMany()+时间范围查询做补充清理 - 如果业务要求“过期前10秒通知”,TTL做不到,得靠应用层轮询或Change Stream监听
MongoDB的TTL索引确实省事,但它的“自动”背后全是隐含前提:字段类型对、值存在、时间算得准。漏掉其中任意一环,数据就卡在那里不动,等你某天发现磁盘悄悄涨满了。











