ttl索引清理非实时,由ttlmonitor线程每60秒扫描执行,延迟1–2分钟;要求字段为合法utc date类型,分片集群需在每个shard手动创建索引,且须定期巡检索引是否存在expireafterseconds元数据。

后台扫描周期固定为60秒,不是实时触发
TTL索引的清理动作由mongod进程内建的TTLMonitor线程执行,该线程默认每60秒唤醒一次,扫描所有TTL索引并执行过期判断。这意味着即使文档已过期1秒,也至少要等满60秒才可能被删除——实际延迟通常在1–2分钟之间。
这个周期不可通过客户端配置修改,只能由MongoDB服务端参数ttlMonitorSleepSecs调整(需重启或动态设置,且影响全局所有TTL任务)。生产环境不建议调低,否则会加剧CPU和I/O压力。
- 用
db.serverStatus().metrics.ttl查passes字段,确认后台线程是否正常轮转 - 如果
passes长期不增长,说明TTLMonitor线程卡住或被阻塞(常见于高负载、OOM或锁竞争)
目标字段不是Date类型或值为空
TTL索引只对满足两个条件的文档生效:字段存在 + 值为合法UTC Date 类型。写入时若存成字符串(如"2026-07-20T08:00:00Z")、数字时间戳或null,该文档将永久豁免清理。
尤其注意Node.js驱动中未显式转换的情况:比如从JSON解析后直接insertOne({ expiresAt: obj.expiresAt }),而obj.expiresAt是字符串。
- 用
db.collection.findOne({ expiresAt: { $exists: true } })抽样检查字段类型 - 用
typeof doc.expiresAt === 'object' && doc.expiresAt.constructor.name === 'Date'验证(shell中可用Object.prototype.toString.call(doc.expiresAt)) - 插入前务必用
new Date(value)或ISODate()标准化
分片集群下TTL索引未在每个分片上单独创建
MongoDB不会自动将TTL索引同步到分片集群的所有分片。如果你只在mongos上运行createIndex,只有config server和当前连接的分片可能生效,其他分片上的数据不会被清理。
必须逐个连接每个shard的primary节点(或使用sh.enableSharding()配套命令),手动为db.collection创建完全一致的TTL索引。
- 查所有分片索引:连到每个shard的primary,运行
db.collection.getIndexes() - 缺失则补:在对应shard上执行
db.collection.createIndex({ expiresAt: 1 }, { expireAfterSeconds: 0 }) - 注意分片键与TTL字段不能冲突——TTL字段不能是分片键的一部分,也不应与分片键强相关(否则可能局部堆积)
索引被意外删除或覆盖
TTL索引本质是普通单字段索引加一个expireAfterSeconds元数据。任何重建索引、collMod操作、或第三方工具(如备份恢复脚本、ORM迁移)都可能删掉它而不提示。
例如用Mongoose的ensureIndexes()或syncIndexes(),若模型定义里没声明TTL选项,旧索引就会被移除。
- 定期巡检:
db.collection.getIndexes()中必须看到"expireAfterSeconds": N字段(N为数值,不是null或undefined) - 避免用
dropIndexes()或通配符重建;改过期时间优先用collMod命令而非删重建 - 上线新版本前,把TTL索引创建逻辑纳入部署检查清单,而不是只靠初始化脚本跑一次
Date,剩下1%的字符串值也会让整个集合的过期逻辑看起来“失效”。











