TTL 索引
注意
如果您正在删除文档以节省存储成本,请考虑在线归档,它位于MongoDB Atlas中。在线归档会自动将不常访问的数据存档到全管理的S3桶中,以实现成本效益的数据分层。
TTL索引是MongoDB可以使用的一种特殊单字段索引,可以在一定时间后或特定时钟时间自动从集合中删除文档。数据过期对于某些类型的信息很有用,例如机器生成的事件数据、日志和会话信息,这些信息只需要在数据库中持久存在有限的时间。
您可以在UI中创建和管理TTL索引,用于托管在MongoDB Atlas中的部署。
创建TTL索引
警告
创建TTL索引后,它可能一次要删除大量符合资格的文档。这种大量工作可能会在服务器上引起性能问题。为了避免这些问题,请计划在非高峰时段创建索引,或在创建索引之前分批删除符合资格的文档。
要创建一个TTL索引,请使用createIndex()
方法。[点击此处查看详情](/docs/manual/reference/method/db.collection.createIndex/#mongodb-method-db.collection.createIndex)。指定一个索引字段,该字段可以是日期类型或包含日期类型值的数组。使用expireAfterSeconds
选项来指定以秒为单位的TTL值。
TTL索引的expireAfterSeconds
值必须在0
到2147483647
之间(包含)。
例如,要在eventlog
集合的lastModifiedDate
字段上创建一个TTL值为3600
秒的TTL索引,请使用以下在mongosh
中进行的操作。[点击此处查看详情](https://mongodb.ac.cn/docs/mongodb-shell/#mongodb-binary-bin.mongosh)
db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )
从MongoDB 6.3版本开始,您可以为时间序列集合创建部分TTL索引。[点击此处查看详情](/docs/manual/core/timeseries-collections/#std-label-manual-timeseries-landing)。这些索引使用集合的timeField
作为键字段,并在metaField
上需要部分过滤表达式。
时间序列集合包含一个可选的expireAfterSeconds
字段。如果您未设置expireAfterSeconds
,则具有部分过滤表达式的TTL索引可让您为与过滤器匹配的文档设置过期期限。如果您设置了expireAfterSeconds
,则部分TTL索引可让您为匹配的文档设置不同的、较短的过期期限。您只能在metaField
上创建部分过滤表达式。
重要
如果集合的expireAfterSeconds
值小于部分TTL索引的expireAfterSeconds
,则集合在较短的时间内删除文档,因此TTL索引无效。
如果时间序列集合包含timeField
时间戳早于1970-01-01T00:00:00.000Z
或晚于2038-01-19T03:14:07.000Z
的文档,则TTL“存活时间”功能不会从集合中删除任何文档。
此天气数据时间序列集合在24小时后删除文档
db.createCollection( "weather24h", { timeseries: { timeField: "timestamp", metaField: "sensor", granularity: "hours" }, expireAfterSeconds: 86400 } )
此TTL索引在1小时后从MongoDB纽约总部天气传感器中删除文档,而不是24小时
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索引的expireAfterSeconds
参数之前,请考虑以下事项
更改
expireAfterSeconds
参数不会触发索引的完全重建。但是,减少expireAfterSeconds
值可以使许多文档有资格立即删除,这可能导致由于删除操作增加而引起性能问题。建议的方法是在更新TTL索引之前手动分批删除文档。这有助于控制对集群的影响。
删除大量文档可能会使存储文件碎片化,从而影响性能。您可能需要在您的集合上运行
compact
命令或执行一个Initial Sync来回收空间并优化存储。
行为
数据过期
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版本开始
为了提高效率,MongoDB可能会将多个文档删除操作一起批量处理。
explain
命令的结果包含一个新的BATCHED_DELETE
阶段,用于批量文档删除。
如果时间序列集合包含timeField
时间戳早于1970-01-01T00:00:00.000Z
或晚于2038-01-19T03:14:07.000Z
的文档,则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索引。
从MongoDB 7.0开始,您可以在时间序列集合的metaField上创建部分TTL索引。在早期MongoDB版本中,您只能为时间序列集合的timeField创建TTL索引。
您不能使用
createIndex()
来更改现有索引中expireAfterSeconds
的值。相反,请使用collMod
数据库命令。有关详细信息,请参阅 更改 TTL 索引的expireAfterSeconds
值。如果一个字段的非 TTL 单字段索引已经存在,则不能在该字段上创建 TTL 索引,因为不能创建具有相同键规范但选项不同的索引。要 将非 TTL 单字段索引更改为 TTL 索引,请使用
collMod
数据库命令。