MongoDB TTL数据未按时删除,通常因字段名、索引配置或时间值不匹配所致:需验证TTL索引是否存在且expireAfterSeconds为正整数、键方向为1、字段名完全一致;时间字段必须为BSON Date类型;仅主节点执行删除;不支持复合索引或嵌套字段。

MongoDB TTL 数据没按时删,大概率不是“坏了”,而是字段、索引或时间值没对齐——先查这三处,90% 的问题当场定位。
确认 TTL 索引是否真正生效
很多人以为 createIndex 执行成功就完事了,其实它可能静默失败或建错。必须手动验证:
- 运行
db.collection.getIndexes(),检查输出里是否有你建的索引,且expireAfterSeconds字段存在并为正整数(比如3600,不能是"3600"或null) - 索引键方向必须是
1(升序),写成-1会导致 TTL 完全不触发 - 字段名要和文档中实际存储的**完全一致**,大小写、下划线都不能错;比如文档存的是
expiredAt,但索引用了expiresAt,那就不会删
检查时间字段类型和值是否合法
TTL 只认 BSON Date 类型,其他一切形式都无效:
- 用
db.collection.findOne({ yourTimeField: { $exists: true } })抽一条看字段值:如果是字符串("2026-07-20T00:00:00Z")、数字(1753027200000)或null,TTL 就跳过该文档 - 正确写法在 Node.js 是
{ expiresAt: new Date() },Python 是{"expiresAt": datetime.now(timezone.utc)} - 特别注意 Go 的
time.Time{}零值(0001-01-01T00:00:00Z)会被当作极早过期时间,导致文档秒删——要用omitempty控制字段不写入,而非传零值
验证过期逻辑是否被后台线程扫描到
TTL 删除不是实时的,但延迟异常长(超过 2 分钟)或完全不动,说明扫描环节卡住了:
- 默认每 60 秒扫描一次,实际延迟通常在 1–2 分钟;可执行
db.runCommand({ serverStatus: 1 }).metrics.ttl查看deletedDocuments和passes计数是否在增长 - 如果集合在副本集上,**只在主节点(primary)上执行删除**;从节点时间不同步(哪怕差几秒)会导致误判——用
db.runCommand({ serverStatus: 1 }).localTime检查各节点时间偏差 - 高负载时 mongod 会主动延迟删除动作,
db.runCommand({ collStats: "collection" })中的count不变但size缓慢下降,就是这个表现
最常被忽略的一点:TTL 不支持复合索引,也不支持嵌套字段(如 "meta.createdAt");哪怕你建了索引,只要字段路径含点号或索引是复合的,expireAfterSeconds 就被彻底忽略——别猜,直接看 getIndexes() 输出。


















