通过mongodb TTL机制让集合中的数据自动过期删除
MongoDB 的集合有衣蛾 TTL (即 time to live,即生存的时间) 的特性。TTL 可以让 mongodb 自动移除过期了的数据,(咦,这岂不是正好符合日志类的数据诉求)。这种机制便比较适合一些 日志数据
机器产生的事件数据
甚至可以用来做 session会话
。
MongoDB 通过一个 TTL 索引来实现这种所谓的 TTL 集合。该特性的实现机制是: mongod 通过一个后台线程去不断的读取集合中某个日期类型的索引,并且移除掉满足过期条件的文档 documents。
下面我们来看如何实操:
创建 TTL 索引
我们是通过 db.collection.createIndex()
命令配合 expireAfterSeconds
选项来对集合中某个字段做 TTL 索引。这个字段必须是 date 类型或者是一个包含 date 类型值的数组字段。
创建 TTL 过期规则的话,有 2 种模式:
- 一种是类似于前端的 maxAge 模式,即设置一个时长,当 mongodb 发现当前时间与时间索引字段的时差超过了 maxAge,则认为过期
- 另一种是类似前端的 expireTime 模式。即设置一个过期时间点,当 mongodb 发现当前系统时间已经超过那个时间点,则认为记录过期
下面我们分别看下两种模式的设置方式。
在一个特定的时长之后删除文档
假如我们 db 中有一个 logs 的集合,其中的记录格式为:
1 | { |
接下来,我们针对 createdAt 这个字段建立一个 TTL 索引:
1 | db.logs.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 }); |
其中 createdAt: 1
表示对 createdAt 字段建立正序的索引。选项 expireAfterSeconds 表示 3600 秒(即 1 小时) 之后过期。其含义是,在 createdAt 字段的值的时刻基础上,再加上 3600 秒 之后的那个时间过期。
测试一把。我让过期时间是 10 秒,做了下测试,以下是测试结果:
在一个特定的时间点来删除文档
有时候,我们不希望在文档创建时刻之后的多少秒再删除。而是希望在每天凌晨某个时刻(例如避开用户的访问高峰期) 进行日志删除。因此,我们希望给文档指定一个特定的过期时间点。这种规则,mongodb 也是可以设置的。我们在创建 TTL 索引时,只需把 expireAfterSeconds 配置的值设为 0 即可.
当然,为了我们的索引字段可以 顾名思义
,因此我们重新设计一下数据源(例如我们把这个 TTL 索引字段取名叫做 expireDeleteTime
)。
数据记录的结构现在如下所示:
1 | { |
然后,我们创建 expireDeleteTime 字段的 TTL 索引,方法如下:
1 | db.logs.createIndex({ expireDeleteTime: 1 }, { expireAfterSeconds: 0 }); |
这样我们就把 expireDeleteTime 字段配置为 TTL 索引,其 expireAfterSeconds 是 0,因此这意味着 mongodb 将用索引字段 【expireDeleteTime 里的时间值 加 0 秒 后的时间】作为判断依据,此时的判断依据其实就是 expireDeleteTime 字段本身。
此时,我们向库中插入一条当前时间的记录(这意味着该记录将会马上过期)。 然后我不断的查询集合,看到结果如下:
最终,数据记录被自动删除了。但是实际上我插入的这条数据应该在插入的那一刻就过期了(因为其 expireAfterSeconds 时间是此刻),但可以发现 mongodb 并不是立刻删除该数据的,而是在大概 60 秒之后才删除,这是因为 mongodb 后台线程的检测间隔是 60 秒。
第二种过期模式对我们的日志数据来说,是很有价值的。我们可以在每条记录入库的时候,就给他添加一个 expireTime
字段,用来标记该记录的过期时间。例如我们期望保留日志记录 15 天,则可以借用 moment 类库这样做:
1 | // 计算出过期时间点 |
MongoDB 将自动删除集合中 expireTime
字段晚于当前时间的记录。因此,可以实现日志 15 天后自动删除的效果。
总结
- MongoDb 可以通过创建 TTL 索引的方式实现过期数据删除
- 当把索引配置项 expireAfterSeconds 即过期时长设置为 0 秒,也就实现了灵活设定文档的过期时间的目的。