注意
如果您为了节省存储成本而删除文档,可以考虑 MongoDB Atlas 中的 Online Archive。Online Archive 可自动将不常访问的数据存档到完全托管的 S3 存储桶,以实现经济高效的数据分层。
“生存时间”(TTL) 索引是特殊的单字段索引, MongoDB可以使用它在一定时间后或在特定时钟时间自动从集合中删除文档。数据过期对于某些类型的信息非常有用,例如机器生成的事件数据、日志和会话信息,这些信息只需在数据库中保留有限的时间。
对于 MongoDB Atlas 中托管的部署,您可以在用户界面中创建和管理 TTL 索引。
创建 TTL 索引
警告
创建 TTL 索引后,可能需要一次性删除大量符合条件的文档。如此大的工作负载可能会导致服务器出现性能问题。为了避免此类问题,请计划在非工作时间创建索引,或者在为将来的文档创建索引之前,批量删除符合条件的文档。
要创建 TTL 索引,请使用 createIndex()。指定一个日期类型或包含日期类型值的数组的索引字段。使用 expireAfterSeconds 选项指定 TTL 值(以秒为单位)。
TTL 索引 expireAfterSeconds 值必须介于 0 和 2147483647(包含两者)之间。
例如,要在 eventlog 集合的 lastModifiedDate 字段上创建 TTL 值为 3600 秒的 TTL 索引,则在 mongosh 中使用以下操作:
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
从 MongoDB 7.0 开始,您可对时间序列集合创建部分 TTL 索引。这些索引使用集合 timeField 作为键字段,并且需要针对 metaField 的部分过滤表达式。
时间序列集合包含可选的 expireAfterSeconds 字段。如果未设置 expireAfterSeconds,则您可以使用带有 partialFilterExpression 的 TTL 索引为与过滤器匹配的文档设置过期期限。如果设置了 expireAfterSeconds,则您也可使用部分 TTL 索引为匹配文档设置较短的过期期限。只能在 metaField 上创建 partialFilterExpression。
重要
如果集合的 expireAfterSeconds 值小于部分 TTL 索引的 expireAfterSeconds,则集合会在较短的时间后删除文档,因此 TTL 索引不起作用。
此天气数据时间序列集合会在 24 小时后删除文档:
db.createCollection( "weather24h", { timeseries: { timeField: "timestamp", metaField: "sensor", granularity: "hours" }, expireAfterSeconds: 86400 } )
此 TTL 索引会在 1 小时(而不是 24 小时)后从 MongoDB NYC 总部天气传感器中删除文档:
db.eventlog.createIndex( { "timestamp": 1 }, { partialFilterExpression: { "sensor": { $eq: "40.761873, -73.984287" } } }, { expireAfterSeconds: 3600 } )
将非 TTL 单字段索引转换为 TTL 索引
从 MongoDB 5.1 开始,您可以将 expireAfterSeconds 选项添加到现有单字段索引。要将非 TTL 单字段索引更改为 TTL 索引,使用 collMod 数据库命令:
db.runCommand({ "collMod": <collName>, "index": { "keyPattern": <keyPattern>, "expireAfterSeconds": <number> } })
以下示例将具有模式 { "lastModifiedDate": 1 } 的非 TTL 单字段索引转换为 TTL 索引:
db.runCommand({ "collMod": "tickets", "index": { "keyPattern": { "lastModifiedDate": 1 }, "expireAfterSeconds": 100 } })
更改 TTL 索引的 expireAfterSeconds 值
要更改 TTL 索引的 expireAfterSeconds 值,请使用 collMod 数据库命令:
db.runCommand({ "collMod": <collName>, "index": { "keyPattern": <keyPattern>, "expireAfterSeconds": <number> } })
以下示例更改 tickets 集合上具有模式 { "lastModifiedDate": 1 } 的索引的 expireAfterSeconds 值:
db.runCommand({ "collMod": "tickets", "index": { "keyPattern": { "lastModifiedDate": 1 }, "expireAfterSeconds": 100 } })
行为
数据过期
TTL 索引在索引字段值后经过指定的秒数后使文档过期。过期阈值为已编入索引的字段值加上指定的秒数。
如果字段为数组,且索引中有多个日期值,MongoDB 会使用数组中最低(最早)日期值计算过期阈值。
对于时间序列集合,当存储桶中的所有文档都过期时,TTL 索引也会删除该桶中的数据。这等于存储桶的时间戳上限加上 expireAfterSeconds 值。例如,如果存储桶涵盖 2023-03-27T18:29:59Z 之前的数据且 expireAfterSeconds 为 300,则 TTL 2023-03-27T18:34:59Z 索引会在以下项后使存储桶过期。
如果文档中的索引字段不包含一个或多个日期值,则该文档不会过期。
如果文档不包含索引字段,则文档将不会过期。
删除操作
mongod 中的后台线程会读取索引中的值,并从集合中删除过期文档。
TTL 线程执行的正在进行的删除操作出现在 db.currentOp() 输出中。随着 TTL 线程删除文档,metrics.ttl.deletedDocuments 服务器状态指标会增加。
从 MongoDB 6.1 开始:
删除进程
TTL 后台删除进程会检查每个 TTL 索引中是否存在过期文档。对于每个 TTL 索引,背景进程会删除文档,直到满足以下条件之一:
该进程从当前索引中删除 50000 个文档。
该进程花费一秒钟从当前索引中删除文档。
所有过期的文档都会从当前索引中删除。
然后,该进程移动到下一个索引。进程遍历每个 TTL 索引一次后,当前子遍历完成,新的子遍历开始检查剩余的过期文档。当 TTL 监控从所有 TTL 索引中删除所有可能的候选文档时,遍历完成。
重要
TTL 删除过程是一个单线程后台任务,这意味着 TTL 删除不是并发的,并且在高工作负载或处理大量过期文档时可能需要更长时间。
此外,进程每隔 60 秒就会停止当前的删除循环,以避免在一次大删除上耗费过多时间。发生这种情况时,当前 sub-pass 结束,新的 sub-pass 开始。
遍历和子遍历分别在 metrics.ttl.passes 和 metrics.ttl.subPasses 服务器状态指标中被跟踪。
删除操作的时间
索引在主节点上完成构建后,MongoDB 会立即开始删除过期文档或时间序列存储桶。有关索引构建过程的更多信息,请参阅在填充集合上构建索引。
TTL 索引不保证过期数据会在过期后立即删除。文档过期时间和 MongoDB 从数据库中删除文档的时间之间可能存在延迟。
删除过期文档的后台任务每 60 秒运行一次。因此,在文档过期和后台任务运行之间的时间段内,文档可能会保留在集合中。MongoDB 在索引完成后 0 到 60 秒开始删除文档。
由于删除操作的持续时间取决于 mongod 实例的工作负载,因此过期数据可能会在后台任务运行之间的 60 秒间隔时间以后存在一段时间。
由 TTL 任务启动的删除操作在前台运行,就像其他删除操作一样。
副本集
在副本集成员上,TTL 后台线程只在成员处于主状态时删除文档。当成员处于从状态时,TTL 后台线程处于空闲状态。从成员复制主成员的删除操作。
支持查询
TTL 索引支持查询的方式与非 TTL 索引相同。
独立模式运行的 mongod
当 mongod 在独立运行模式运行且 system.local.replset集合包含数据时, TTL 监控会停止。如果将副本集节点从副本集中取出并将其作为独立运行节点运行,则 TTL 监控将被禁用。
TTL 和模式验证
即使 TTL 索引不需要模式验证,使用验证可以通过标准化用于过期的日期字段的存在和格式来帮助确保行为的一致性。
例如,您可以使用模式验证来实施 lastModifiedDate 字段的存在,并确保其值符合有效的日期格式:
db.createCollection( "eventlog", { validator: { $jsonSchema: { bsonType: "object", required: [ "lastModifiedDate" ], properties: { lastModifiedDate: { bsonType: "date", description: "Must be a valid date." } } } } } )
此模式验证规则确保:
eventlog集合中的每个文档都包含lastModifiedDate字段。lastModifiedDate字段包含有效的日期值。
提示
限制
TTL 索引是单字段索引。复合索引不支持 TTL,并且会忽略
expireAfterSeconds选项。_id字段不支持 TTL 索引。从 MongoDB 7.0开始,您可以在时间序列集合的
metaField上创建一个部分 TTL 索引。在早期 MongoDB 版本中,您只能为时间序列集合的timeField创建 TTL 索引。不能使用
createIndex()更改现有索引的expireAfterSeconds值。请使用collMod数据库命令。有关详情,请参阅更改 TTL 索引的expireAfterSeconds值。如果某个字段已存在非 TTL 单字段索引,则无法对同一字段创建 TTL 索引,因为无法创建具有相同键规范且仅选项不同的索引。要将非 TTL 单字段索引更改为 TTL 索引,请使用
collMod数据库命令。