TTL Indexes
注意
如果您为了节省存储成本而删除文档,可以考虑 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 6.3 开始,可以在时间序列集合上创建部分 TTL 索引。这些索引使用集合 timeField
作为键字段,并且需要在 metaField
上使用部分过滤器表达式。
时间序列集合包括一个可选的expireAfterSeconds
字段。如果您不设置expireAfterSeconds
,则带有 的 TTLpartialFilterExpression
索引允许您为与筛选器匹配的文档设置过期期限。如果您确实设置了expireAfterSeconds
,则部分 TTL 索引允许您为匹配文档设置另一个更短的过期时间。您只能在 上创建partialFilterExpression
metaField
。
重要
如果集合的 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秒停止一次当前的删除循环,以防止在单个大删除上花费过多时间。发生这种情况时,当前子通道结束,新的子通道开始。
传递和子传递分别在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,并且会忽略
expireAfterSeconds
选项。_id
字段不支持 TTL 索引。从 MongoDB7 开始。0 ,您可以在 时间序列集合 的 上创建 部分 TTL 索引
metaField
。在早期 MongoDB 版本中,您只能为时间序列集合的 创建 TTLtimeField
索引。您不能使用
createIndex()
更改现有索引的expireAfterSeconds
值。请改用collMod
数据库命令。有关详情,请参阅更改 TTL 索引的expireAfterSeconds
值。如果某个字段已存在非 TTL 单字段索引,则无法对同一字段创建 TTL 索引,因为无法创建具有相同键规范且仅选项不同的索引。要将非 TTL 单字段索引更改为 TTL 索引,请使用
collMod
数据库命令。