MongoDB 的存活时间(TTL)集合功能,可以让 mongod 在指定秒数之后,或指定时刻自动删除数据。它适用于只需保留有限时间的机器事件、日志与会话等信息。
TTL 依靠特殊索引属性,以及 mongod 后台线程实现。后台线程读取索引中的日期值,并从集合删除过期文档。
使用 createIndex() 创建 TTL 索引,索引字段必须是日期类型,或包含日期值的数组,并通过 expireAfterSeconds 指定秒数。
注意:TTL 只支持单字段索引,复合索引不支持此属性。可通过 collMod 修改已有 TTL 索引的 expireAfterSeconds。
在 MongoDB Atlas 界面中设置过期
1. 打开项目的 Clusters 页面
必要时,先从导航栏 Organizations 菜单选择组织,再从 Projects 选择项目。在侧栏 Database 下点击 Clusters。
2. 进入集合
在目标集群点击 Browse Collections,再从左侧依次选择数据库和集合。
3. 打开创建索引对话框
点击 Indexes,再点击 Create Index。
4. 创建带 expireAfterSeconds 的索引
在 Fields 中填写索引键。本例索引 expiresAfter:
{ "expiresAfter": 1 }
在 Options 中指定 expireAfterSeconds。以下设置让数据在 expiresAfter 的值之后一秒过期:
{ expireAfterSeconds: 1 }
点击 Review,再点击 Confirm。
5. 添加包含 expiresAfter 的文档
从左侧选择集合,点击 Find,再点击 Insert Document。在 _id 下方字段框输入 expiresAfter,旁边填写:
2023-10-01T12:00:00.000+00:00
该值对应 2023 年 10 月 1 日 12:00。将数据类型下拉框改为 Date,再点击 Insert。
文档在此时间之后一秒过期。原文说明界面中的删除可能需要一到两秒,也可能需要刷新才能看到结果。
指定若干秒后过期
可以通过集合创建选项,或 TTL 索引设置。
创建集合时指定
创建集合时使用 expireAfterSeconds。此集合级选项仅适用于时间序列集合和聚簇集合。
使用 TTL 索引
在 BSON 日期字段或日期数组上创建 TTL 索引,设置非负的 expireAfterSeconds。当距离索引字段时间超过指定秒数,文档过期。
有效值为 0 到 2147483647,包含两端。
以下在 log_events.createdAt 上创建索引,设置十秒后过期:
db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 10 } )
插入时把 createdAt 设为当前时间:
db.log_events.insertOne( {
"createdAt": new Date(),
"logEvent": 2,
"logMessage": "Success!"
} )
当 createdAt 距当前时间超过指定秒数,MongoDB 自动删除文档。如果字段是日期数组,只要其中至少一个日期超过过期时长,文档就会过期。
带筛选条件的过期
可以创建同时具有 partial 与 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}
] )
等待十秒后查询:
db.foo.find({}, {_id: 0, F: 1, D: 1})
匹配 { D : 1 } 的文档已经过期删除,只剩:
{ "F" : ISODate("2019-03-07T20:59:18.428Z"), "D" : 3}
在指定时刻过期
在日期字段或日期数组上创建 TTL 索引,将 expireAfterSeconds 设为 0。每个文档的索引日期就是它应过期的时刻。日期在过去时,MongoDB 将其视为已过期。
例如:
db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )
为每个文档设置 expireAt。以下文档在 2013 年 7 月 22 日 14:00:00 过期:
db.log_events.insertOne( {
"expireAt": new Date('July 22, 2013 14:00:00'),
"logEvent": 2,
"logMessage": "Success!"
} )
当 expireAt 比当前时间早于配置秒数时自动删除;此处秒数为 0。
expireAfterSeconds 被设为 NaN 的索引
警告:可能丢失数据。TTL 索引的 expireAfterSeconds 为 NaN 时,升级、降级和某些同步操作可能出现意外行为并丢失数据。不要这样配置。
- MongoDB 5.0 之前:记录错误,不删除记录。
- MongoDB 5.0.0 至 5.0.13,以及 6.0.0 至 6.0.1:NaN 被视为 0,全部 TTL 索引文档立即过期。
- 从 5.0.14 和 6.0.2 起:服务器不使用
expireAfterSeconds为 NaN 的 TTL 索引。
仍可能导致文档意外过期的情况包括:
- 从 5.0.0–5.0.13 或 6.0.0–6.0.1 向更旧版本执行初始同步。
- 从更旧版本升级到 5.0.0–5.0.13。
- 把 5.0 之前的 mongodump 集合恢复到 5.0.0–5.0.13 或 6.0.0–6.0.1。
应删除或纠正配置错误的 TTL 索引。
1. 找出配置错误的索引
在 mongosh 中运行以下脚本。它不适用于旧 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();
2. 修正索引
使用 collMod 更新脚本找到的错误值。也可以删除这些 TTL 索引,之后通过 createIndexes 重新创建。
原文:Expire Data from Collections by Setting TTL。作者/维护方:MongoDB 文档维护者。本文为中文翻译,代码及命令保留原文。











暂无评论内容