通过设置 TTL 使集合中的数据过期
本文档介绍了 MongoDB 的“生存时间”或 TTL 集合功能。TTL 集合可以将数据存储在 MongoDB 中,并让 mongod
在指定的秒数后或在特定的时钟时间自动删除数据。
您可以使托管在以下环境中的部署的数据过期:
MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务
MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本
MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本
数据过期对于某些类别的信息很有用,包括机器生成的事件数据、日志和仅需要保留一段有限时间的会话信息。
特殊的 TTL 索引属性支持 TTL 集合的实施。TTL 功能依赖于 mongod
中的后台线程。该线程可读取索引中的日期类型值并从集合中删除过期的文档。
要创建 TTL 索引,请使用 createIndex()
。指定一个日期类型或包含日期类型值的数组的索引字段。使用 expireAfterSeconds
选项指定 TTL 值(以秒为单位)。
注意
TTL 索引为单字段索引。复合索引不支持 TTL 属性。有关 TTL 索引的更多信息,请参阅 TTL 索引。
您可以使用 collMod
命令修改现有 TTL 索引的 expireAfterSeconds
。
如果时间序列集合包含时间戳在 1970-01-01T00:00:00.000Z
之前或 2038-01-19T03:14:07.000Z
之后为 timeField
的文档,则 TTL“生存时间”功能不会从集合中删除任何文档。
使 MongoDB Atlas 用户界面中的文档过期
要使 Atlas 用户界面中的数据过期,请按照以下步骤操作:
在MongoDB Atlas用户界面中,转到项目的Clusters 页面。
如果尚未显示,请从导航栏上的 Organizations 菜单中选择包含所需项目的组织。
如果尚未显示,请从导航栏的 Projects 菜单中选择您的项目。
如果尚未显示,请单击侧边栏中的Clusters 。
会显示集群页面。
将包含expiresAfter
字段的文档添加到集合中
在左侧导航窗格中,选择包含索引的集合。
单击 Find 标签页。
单击 Insert Document(连接)。
单击 _id 字段下的文本字段,然后输入字段名称
expiresAfter
。单击
expiresAfter
旁边的文本字段,输入以下值:2023-10-01T12:00:00.000+00:00 该值在 2023 年 10 月 1 日 12:00 后使数据过期。
单击数据类型下拉菜单,然后将数据类型值更改为 Date。
单击 Insert(连接)。
该文档将在
expiredAfter
字段的值过后一秒钟自动过期。TTL 索引可能需要 1-2 秒才能使文档过期。您可能需要刷新 UI 才能看到 MongoDB Atlas 删除过期文档。
在指定的秒数后使文档过期
您可以在终端中设置数据过期时间为指定秒数。若要在索引字段设置数据过期时间为指定秒数后,请在保存 BSON 日期类型值的字段,或 BSON 日期类型对象数组上创建 TTL 索引,并在 expireAfterSeconds
字段中指定一个非零的正值。当 expireAfterSeconds
字段中的秒数超过文档索引字段中指定的时间时,文档将过期失效。[1]
TTL 索引 expireAfterSeconds
值必须介于 0
和 2147483647
(包含两者)之间。
例如,以下操作在 log_events
集合的 createdAt
字段上创建索引,并指定 10
的 expireAfterSeconds
值,以将过期时间设置为 createdAt
指定的时间后十秒。
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 10 } )
将文档添加到 log_events
集合时,将 createdAt
字段设置为当前时间:
db.log_events.insertOne( { "createdAt": new Date(), "logEvent": 2, "logMessage": "Success!" } )
当文档的 createdAt
值 [1] 早于 expireAfterSeconds
中指定的秒数时,MongoDB 将自动从 log_events
集合中删除文档。
[1] | (1, 2) 如果该字段包含 BSON 日期类型对象的数组,则如果至少一个 BSON 日期类型对象早于 expireAfterSeconds 中指定的秒数,则数据将过期。 |
使用筛选条件使文档过期
要使用特定筛选器表达式使文档过期,您可以创建一个既是部分索引又是 TTL 索引的索引。
创建部分 TTL 索引:
db.foo.createIndex( { F: 1 }, { name: "Partial-TTL-Index", partialFilterExpression: { D : 1 }, expireAfterSeconds: 10 } )
插入两个文档,其中一个符合 partialFilterExpression
的过滤器表达式 { D : 1 }
:
db.foo.insertMany( [ { "F" : ISODate("2019-03-07T20:59:18.428Z"), "D" : 3}, { "F" : ISODate("2019-03-07T20:59:18.428Z"), "D" : 1} ] )
等待十秒钟,然后查询 foo
集合:
db.foo.find({}, {_id: 0, F: 1, D: 1})
与 { D : 1 }
的 partialFilterExpression
匹配的文档已删除(已过期)。因此, foo
集合中仅保留一份文档:
{ "F" : ISODate("2019-03-07T20:59:18.428Z"), "D" : 3}
在特定时钟时间使文档过期
您可以在终端的指定时钟时间使数据过期。要在特定时钟时间使文档过期,请首先在保存 BSON 日期类型值或 BSON 日期类型对象数组的字段上创建 TTL 索引,并指定expireAfterSeconds
值0
。 对于集合中的每个文档,将索引日期字段设置为与文档到期时间相对应的值。 如果索引日期字段包含过去的日期,MongoDB 会认为该文档已过期。
例如,以下操作在 log_events
集合的 expireAt
字段上创建索引并指定 0
的 expireAfterSeconds
值:
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
对于每个文档,将 expireAt
的值设置为与文档应过期的时间相对应。例如,下面的insertOne()
操作添加了一个在July 22, 2013 14:00:00
过期的文档。
db.log_events.insertOne( { "expireAt": new Date('July 22, 2013 14:00:00'), "logEvent": 2, "logMessage": "Success!" } )
当文档的 expireAt
值早于 expireAfterSeconds
中指定的秒数(在本例中为早于 0
秒)时,MongoDB 将自动从 log_events
集合中删除文档。 因此,数据在指定的expireAt
值处过期。
使用 NaN 配置的索引
警告
可能的数据丢失
当 TTL 索引将 expireAfterSeconds
设置为 NaN
时,升级、降级和某些同步操作可能会导致意外行为并可能导致数据丢失。
请勿在 TTL 索引配置中将 expireAfterSeconds
设置为 NaN
。
在 MongoDB 5.0 之前,当 TTL 索引将 expireAfterSeconds
设置为 NaN
时,MongoDB 会记录错误并且不会删除任何记录。
从 MongoDB 5.0.0 - 5.0.13(和 6.0.0 - 6.0.1),NaN
被视为 0
。如果通过将 expireAfterSeconds
设为 NaN
来配置某一 TTL 索引,则使用 TTL 进行索引的所有文档均会立即过期。
从 MongoDB 5.0.14(和 6.0.2)开始,服务器将不再使用将 expireAfterSeconds
设置为 NaN
的 TTL 索引。
不过,在某些情况下仍可能出现意外行为。文件可能过期:
在从 MongoDB 5.0.0 - 5.0.13(或 6.0.0 - 6.0.1)初始同步到早期版本期间。
当从早期版本升级到 MongoDB 5.0.0 - 5.0.13 时。
从前5.0 将
mongodump
导入 MongoDB 5.0.0 - 5 。 0 。 13 (或6 . 0 . 0 - 6 。 0 。 1 ) 实例。
为了避免出现问题,请删除或更正任何配置错误的 TTL 索引。
识别配置错误的索引。
在 mongosh
Shell 中运行以下脚本。该脚本无法在旧版 mongo
shell 中运行。
function getNaNIndexes() { const nan_index = []; const dbs = db.adminCommand({ listDatabases: 1 }).databases; dbs.forEach((d) => { if (d.name != 'local') { const listCollCursor = db .getSiblingDB(d.name) .runCommand({ listCollections: 1 }).cursor; const collDetails = { db: listCollCursor.ns.split(".$cmd")[0], colls: listCollCursor.firstBatch.map((c) => c.name), }; collDetails.colls.forEach((c) => db .getSiblingDB(collDetails.db) .getCollection(c) .getIndexes() .forEach((entry) => { if (Object.is(entry.expireAfterSeconds, NaN)) { nan_index.push({ ns: `${collDetails.db}.${c}`, index: entry }); } }) ); } }); return nan_index; }; getNaNIndexes();
更正配置错误的索引。
使用 collMod
命令更新脚本发现的任何错误配置的 expireAfterSeconds
值。
作为替代方案,您可以 drop
任何配置错误的 TTL 索引,并稍后使用 createIndexes
命令重新创建这些索引。