TTL Indexes
注意
如果您为了节省存储成本而删除文档,可以考虑 MongoDB Atlas 中的 Online Archive。Online Archive 可自动将不常访问的数据存档到完全托管的 S3 存储桶,以实现经济高效的数据分层。
TTL 索引是特殊的单字段索引,MongoDB 可以在一定时间后或在特定时钟时间使用这种索引自动从集合中删除文档。数据过期对于某些类型的信息(例如机器生成的事件数据、日志和会话信息)很有用,这些信息仅需要在数据库中保留有限的时间。
您可以 在 UI 中为 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 6.3 开始,可以在时间序列集合上创建部分 TTL 索引。这些索引使用集合 timeField
作为键字段,并且需要在 metaField
上使用部分过滤器表达式。
时间序列集合包含可选的 expireAfterSeconds
字段。如果未设置 expireAfterSeconds
,则您可以使用带有 partialFilterExpression
的 TTL 索引为与过滤器匹配的文档设置过期期限。如果设置了 expireAfterSeconds
,则您也可使用部分 TTL 索引为匹配文档设置较短的过期期限。只能在 metaField
上创建 partialFilterExpression
。
重要
如果集合的 expireAfterSeconds
值小于部分 TTL 索引的 expireAfterSeconds
,则集合会在较短的时间后删除文档,因此 TTL 索引不起作用。
如果时间序列集合包含时间戳在 1970-01-01T00:00:00.000Z
之前或 2038-01-19T03:14:07.000Z
之后为 timeField
的文档,则 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 } })
expireAfterSeconds
更改TTL索引的 值
要更改 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 开始:
如果时间序列集合包含时间戳在 1970-01-01T00:00:00.000Z
之前或 2038-01-19T03:14:07.000Z
之后为 timeField
的文档,则 TTL“生存时间”功能不会从集合中删除任何文档。
删除进程
TTL 后台删除进程会检查每个 TTL 索引中是否存在过期文档。对于每个 TTL 索引,背景进程会删除文档,直到满足以下条件之一:
该进程从当前索引中删除 50000 个文档。
该进程花费一秒钟从当前索引中删除文档。
所有过期的文档都会从当前索引中删除。
然后,该进程移动到下一个索引。进程遍历每个 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 索引相同。
限制
TTL 索引是单字段索引。复合索引不支持 TTL,并且会忽略
expireAfterSeconds
选项。_id
字段不支持 TTL 索引。从 MongoDB 7.0开始,您可以在时间序列集合的
metaField
上创建一个部分 TTL 索引。在早期 MongoDB 版本中,您只能为时间序列集合的timeField
创建 TTL 索引。不能使用
createIndex()
更改现有索引的expireAfterSeconds
值。请使用collMod
数据库命令。有关详情,请参阅更改 TTL 索引的expireAfterSeconds
值。如果某个字段已存在非 TTL 单字段索引,则无法对同一字段创建 TTL 索引,因为无法创建具有相同键规范且仅选项不同的索引。要将非 TTL 单字段索引更改为 TTL 索引,请使用
collMod
数据库命令。