通过设置 TTL 从集合中过期数据
本文档介绍了 MongoDB 的 "生存时间" 或TTL 集合功能。TTL 集合使您能够在 MongoDB 中存储数据,并在指定秒数或特定时间后自动删除数据。
您可以在以下环境中为部署过期数据
MongoDB Atlas:云中 MongoDB 部署的全托管服务
MongoDB 企业版:MongoDB 的基于订阅、自主管理的版本
MongoDB 社区版: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 UI 中过期文档
要在 Atlas UI 中过期数据,请按照以下步骤操作
在 MongoDB Atlas UI 中,转到集群页面,用于您项目。
如果尚未显示,请从导航栏的 组织 菜单中选择包含您所需项目的组织。
如果尚未显示,请从导航栏中的 项目 菜单中选择您的项目。
如果尚未显示,请点击侧边栏中的 集群。
集群 页面 将显示。
指定秒数后过期文档
您可以在终端中指定秒数后过期数据。要自索引字段创建以来指定秒数后过期数据,请在一个字段上创建TTL索引,该字段包含BSON日期类型或BSON日期类型对象的数组,并在expireAfterSeconds
字段中指定一个正的非零值。文档将在从其索引字段中指定的时间开始计算,经过expireAfterSeconds
字段指定的秒数后过期。[1]
TTL索引的expireAfterSeconds
值必须在0
和2147483647
之间(包括)。
例如,以下操作在log_events
集合的createdAt
字段上创建索引,并将expireAfterSeconds
值指定为10
,将过期时间设置为自createdAt
指定的时间起10秒。
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 10 } )
在向log_events
集合添加文档时,将createdAt
字段设置为当前时间
db.log_events.insertOne( { "createdAt": new Date(), "logEvent": 2, "logMessage": "Success!" } )
MongoDB将在文档的createdAt
值[1]早于在expireAfterSeconds
中指定的秒数时自动删除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
字段上创建索引,并指定了expireAfterSeconds
值为0
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
对于每个文档,将expireAt
的值设置为对应于文档应该过期的值。例如,以下insertOne()
操作添加一个在2013年7月22日 14:00:00
过期的文档。
db.log_events.insertOne( { "expireAt": new Date('July 22, 2013 14:00:00'), "logEvent": 2, "logMessage": "Success!" } )
MongoDB会在文档的expireAt
值超过在expireAfterSeconds
中指定的秒数时自动删除log_events
集合中的文档,即在这种情况下比指定值早0秒。因此,数据在指定的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
。如果一个TTL索引的expireAfterSeconds
设置为NaN
,则所有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。
从MongoDB 5.0.0 - 5.0.13(或6.0.0 - 6.0.1)的实例中恢复从5.0之前的
mongodump
集合。
为了避免问题,请删除或更正任何配置错误的TTL索引。
识别配置错误的索引。
在 mongosh
壳中运行以下脚本。该脚本在传统的 mongo
壳中无法工作。
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
命令重新创建它们。