1. 前言
在近期的工作中,使用了MongoDB
来保存了一些日志数据,但是这些日志数据具有一定的时效性,也就是按照业务的需要,保存xx天即可,在超过了这个时间之后,需要删除过时的日志。
按照之前的做法,可以使用定时任务,例如:Spring的定时器,xxl-job等工具,在每天凌晨的时候删除数据,这种方法虽然可行,但是需要额外的编码工作。
那有没有一种方式能够简单的搞定过期数据的清理呢?
当然,MongoDB
提供了一种TTL
索引的机制,可以非常方便的处理这种需求,下面是官方文档,有阅读能力的同学可以直接看这个文档。
《Expire Data from Collections by Setting TTL》
2.概念与使用
TTL的全称为 “Time To Live”,就是存活时间的意思,这是计算机中一个非常常见的概念,经常用于一些需要延时处理的任务,熟悉消息队列的同学肯定知道,延时队列实际上也是TTL的一种实践方式。
结合这个概念,MongoDB通过给数据创建TTL Index 也就是TTL索引的方式,在完成数据的过期和清理的任务。
2.1.使用方式
TTL Index 的使用方法的非常简单,只需要找到一个 日期类型 字段,给这个字段创建索引,并指定存活时间(expireAfterSeconds
)就可以了。
我这里使用的MongoDB版本是最新的7.0.6
,可以通过db.version();
查看当前的版本号,太低的版本可能不支持这个功能。
先准备一条数据,然后创建索引:
db.log_test.insertOne({
"createTime": new Date(),
"msg": "Success!"
})
db.log_test.find();
db.log_test.createIndex({ "createTime": 1 }, { expireAfterSeconds: 10 });
db.log_test.getIndexes();
这里的expireAfterSeconds
设置的是存活时间,单位:秒,在设置完成后,createTime
与当前系统时间的差值大于 10s 的数据,将会被清理掉。是不是非常简单。
2.2.数组中包含日期字段
在上面已经创建索引的基础上, 看一下下面这个语句:
db.log_test.insertOne({
"createTime": [new Date(),'asdf',111],
"msg": "Success!"
});
猜一下,这样的数据还能够被自动清理吗?
实际上,这种数据会可以被自动清理的,即使字段值是一个数组,但它包含了日期类型的字段,TTL索引都能够生效。
那再进一步,如果数组中包含了多个日期字段呢?该使用哪一个日期呢?例如:
db.log_test.insertOne({
"createTime": [new Date,ISODate("2024-04-20T13:48:18.428Z")],
"msg": "Success!"
});
这种情况,数据也会在10秒后被删除掉。无关乎日期的大小,只要有任意一个日期满足过期的要求,就可以被删掉。
2.3.设置具体的过期时间点
上面说的都是设置一个数据的存活时间,不是固定的过期时间,如果想要设置固定的过期时间也非常简单,只需要把索引做一下变换。
db.log_test.createIndex({ "expireTime": 1 }, { expireAfterSeconds: 0 });
将expireAfterSeconds
设置为0,然后在expireTime
字段上设置具体的时间就可以了。
2.4.额外的过滤条件
上述的情况都是针对全量的数据做过期处理,但可能有的时候我们可能想保留有些有价值的数据,这部分数据不加入自动过期的逻辑。这个时候可以在创建索引的时候加入部分过滤条件,如下:
db.log_test.createIndex(
{"createTime": 1},
{
name:"canExpireDataTTL",
partialFilterExpression:{"canExpire":true}, // canExpire=true的字段会过期自动清理
expireAfterSeconds: 10
}
);
接下来插入两条数据:
db.log_test.insertOne({
"createTime": [new Date()],
"msg": "Success!",
"canExpire":true
});
db.log_test.insertOne({
"createTime": [new Date()],
"msg": "Success!",
"canExpire":false
});
按照预期,在10秒后,canExpire
为true
的数据将会被清理,false
的数据将会保留。验证结果如下:
3.总结
TTL索引可以在不添加新的代码逻辑的情况下,非常简单清理已过期的数据,只需要在日期类型的字段上创建TTL Index就可以了,即使这个字段是数组,只要数组中有日期也可能生效。
需要注意的是:过期数据的清理工作是由MongoDB的后台线程周期性执行的,一般这个周期是60s执行一次,如果发现上述的数据在 10秒后还没有被删除的话也不用惊慌,数据将会在60s的周期内被清理掉。