MongoDB提供了TTL索引自动从集合中删除过期文档,这个功能在存储日志
等一些只需要在固定时间内存储数据的场景下使用起来非常方便。最近用了一下,发现这个功能没有想象中的那么好。
TTL索引的建立很简单,要求是只能在一个field
上创建,并且这个field
是一个Date类型数据或者是一个包含Date类型数据的Array。
在创建索引的时候只需要多增加一个expireAfterSeconds
参数,在field
加上这个expireAfterSeconds
的时间大于当前时间后,这个文档就会被自动删除。
TTL索引相比一般索引还多了一些合理的限制,比如:
- 必须是单个字段(field),多字段不支持
_id
字段不支持TTL索引定长集合(Capped Collections)
不支持- 不能使用createIndex()来改变已有索引的
expireAfterSeconds
值。可以使用collMod尝试,失败则只能删除并重建索引 - 当目标字段(field)已有非TTL索引,必须先删除旧的,再重建新的TTL索引。不能建立一个只有这个参数(带有
expireAfterSeconds
)不同的索引
TTL索引有一般索引的功能,上面的限制大多数情况下能让人接受。但是因为TTL索引的删除机制,想要好好的用它,还需要斟酌。
首先说一下TTL索引的实现方式:mongod
每60s会启动一个后台线程来删除过期文档,这就意味这MongoDB的TTL不同于Redis,是一定有延迟的。
而且这个60s也不是硬性保证的,万一节点压力非常大,过期文档的删除时间会超过60s。
MongoDB只保证文档最后一定会删除,在没有删除之前,即使过了失效时间,文档依旧可以查询。
在副本集的情况下,TTL后台线程只会在主节点删除文档,然后通过主节点把删除操作同步到从节点。但是每个从节点自身也会执行自己的TTL后台线程。
当主节点压力很大,从节点压力小的时候,就会出现过期文档在主节点依旧可以查询,在从节点却已被TTL索引删除的情况。
索引的建立方式也会对这个造成影响。为了避免对读写造成堵塞,我一般会使用background
后台建立索引。
当使用background
方式建立索引,TTL线程可以在建立索引的过程中删除过期文档。而foreground
则要等到索引建立完成才可以开始删除。
TTL Indexes
和Capped Collections
实现的功能其实有点类似,一个是是限定时间,一个是限定大小,但是TTL Indexes
的不确定性大了很多。要使用他们肯定要根据具体的场景,我更倾向于使用确定的Capped Collections
。