文档菜单
文档首页
/
MongoDB 手册
/ / /

通过设置 TTL 从集合中过期数据

本页内容

  • 在 MongoDB Atlas UI 中过期文档
  • 在指定秒数后过期文档
  • 根据过滤条件过期文档
  • 在特定时钟时间过期文档
  • 使用 NaN 配置的索引

本文档介绍了 MongoDB 的 "生存时间" 或TTL 集合功能。TTL 集合使您能够在 MongoDB 中存储数据,并在指定秒数或特定时间后自动删除数据。

您可以在以下环境中为部署过期数据

  • MongoDB Atlas:云中 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 “生存时间”功能不会从集合中删除任何文档。

要在 Atlas UI 中过期数据,请按照以下步骤操作

1
  1. 如果尚未显示,请从导航栏的 组织 菜单中选择包含您所需项目的组织

  2. 如果尚未显示,请从导航栏中的 项目 菜单中选择您的项目。

  3. 如果尚未显示,请点击侧边栏中的 集群

    集群 页面 将显示。

2
  1. 对于包含您希望过期的数据的集群,点击 浏览集合

  2. 在左侧导航窗格中,选择数据库。

  3. 在左侧导航窗格中,选择集合。

3
  1. 点击索引标签页。

  2. 点击创建索引

4
  1. 字段部分,输入索引键规范文档。对于本示例,输入以下文本以在expiresAfter字段上创建索引

    { "expiresAfter": 1 }
  2. 选项部分,输入expireAfterSeconds选项。对于本示例,输入以下文本以在expiresAfter字段值后1秒过期数据

    { expireAfterSeconds: 1 }
  3. 点击审查

  4. 点击确认

5
  1. 在左侧导航窗格中,选择包含索引的集合。

  2. 点击查找标签页。

  3. 点击插入文档

  4. 点击_id字段下的文本字段,并输入字段名expiresAfter

  5. 点击expiresAfter旁边的文本字段,并输入以下值

    2023-10-01T12:00:00.000+00:00

    该值在2023年10月1日12:00后过期。

  6. 点击数据类型下拉菜单,将数据类型值更改为日期

  7. 点击插入

    文档将在expiredAfter字段值后自动过期1秒。

    时间到索引可能需要1-2秒来过期文档。您可能需要刷新UI才能看到MongoDB Atlas已删除过期的文档。

您可以在终端中指定秒数后过期数据。要自索引字段创建以来指定秒数后过期数据,请在一个字段上创建TTL索引,该字段包含BSON日期类型或BSON日期类型对象的数组,并在expireAfterSeconds字段中指定一个正的非零值。文档将在从其索引字段中指定的时间开始计算,经过expireAfterSeconds字段指定的秒数后过期。[1]

TTL索引的expireAfterSeconds值必须在02147483647之间(包括)。

例如,以下操作在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值时过期。

警告

可能的数据丢失

当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索引。

1

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();
2

使用 collMod 命令来更新脚本发现的任何配置错误的 expireAfterSeconds 值。

作为替代方案,您可以使用 drop 任何配置错误的 TTL 索引,稍后使用 createIndexes 命令重新创建它们。

返回

TTL