MongoDB 索引过期自动删除

MongoDB 的 TTL(time to live)索引,过期自动删除

MongoDB 索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。
这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。
索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构

MongoDB 的 maxTimeStamp

Stack Overflow - What is the maximum time.Time in Go?

在 Go 中的最大值时间可以使用var maxTime = time.Unix(1<<63-62135596801, 999999999)
time.Time 在 go 中是用一个 64 位的整型加上一个3264 位的纳秒值(现在的版本是 64 位)组成的,如果使用time.Unix(1<<63-62135596801, 999999999)将会触发 sec 的组件的整型溢出,而且比较操作诸如time.Before()也将不会正常工作.
这是因为time.Unix(sec,nsec)将 sec 组件中秒的偏移量增加 62135596800 秒,代表 1 年(公元 1 年,Go 中为零时)和 1970 之间(公元 1970 年 Unix 中为零时)之间的秒数。

这里是使用时间戳(int)的方式,如何只是MongoDB里面Date,自己定义一个类似9999-11-31 23:59:59

MongoDB 的 TTL(time to live)

MongoDB 创建数据库

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 首先在本地运行MongoDB
$ sudo mongod
# 然后开启另一个终端进行连接
$ mongo
# 首先创建一个索引,通过createdAt来进行标定,设置90秒之后自动删除
> db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 90 } )
{
"createdCollectionAutomatically" : true,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
# 就可以查询到里面的索引信息了
> db.log_events.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "test.log_events"
},
{
"v" : 2,
"key" : {
"createdAt" : 1
},
"name" : "createdAt_1",
"ns" : "test.log_events",
"expireAfterSeconds" : 90
}
]
# 然后插入一条数据
> db.log_events.insert( { "createdAt": new Date(), "logEvent": 2, "logMessage": "Success!" } )
WriteResult({ "nInserted" : 1 })

做完这些操作,那条数据就会在 90 秒内删除,这个删除可能会滞后 60s,具体看官方文档的介绍–>Timing of the Delete Operation

问题

对于 indexes 的重复插入的问题,在代码中进行重复的插入也会报 panic,不知道是不是因为 MongoDB 的 sdk 比较旧的原因,还需要注意在 MongoDB 新版中已经使用 ensureIndex,createIndex 是其别名

1
2
3
4
5
6
7
8
> db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 90 } )
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 2,
"numIndexesAfter" : 2,
"note" : "all indexes already exist",
"ok" : 1
}

删除操作用时(Timing of the Delete Operation)

TTL 索引-Timing of the Delete Operation

When you build a TTL index in the background, the TTL thread can begin deleting documents while the index is building. If you build a TTL index in the foreground, MongoDB begins removing expired documents as soon as the index finishes building.
(这段抄的 Google 翻译,逃):
在后台构建 TTL 索引时,TTL 线程可以在索引构建时开始删除文档。 如果在前台构建 TTL 索引,MongoDB 会在索引完成构建后立即删除过期的文档。

TTL 索引不能保证过期数据会被立刻删除。在文档过期和 MongoDB 从数据库中删除文档之间,可能会有延迟。
删除过期数据的后台任务每隔 60 秒 运行一次。所以,在文档过期 之后 和 后台任务运行或者结束之前,文档会依然存在于集合中。
删除操作的持续实际取决于您的 mongod 实例的负载。因此,在两次后台任务运行的间隔间,过期数据可能会继续留在数据库中 超过 60 秒。

go 语言操作

1
2
3
4
5
6
7
8
9
10
11
//插入indexes
sessionTTL := mgo.Index{
Key: []string{"createdAt"},
Unique: false,
DropDups: false,
Background: true,
ExpireAfter: 90 * time.Second} // one hour

if err := appColl.EnsureIndex(sessionTTL); err != nil {
panic(err)
}

参考资料

如果对您有帮助,请我喝杯咖啡?