Skip to main content

MongoDB的 TTL 索引

TTL索引是特殊的单字段索引,MongoDB可以用来在一定时间之后或者在一个特定的时钟时间自动删除集合中的文档。对于特定类型的信息(例如机器生成的行为数据、日志以及会话信息,只需要将信息存储在数据库中一定时间)而言,数据过期是非常有效的。

可以使用db.collection.createIndex()方法和expireAfterSeconds方法来在某个字段(字段值为date或包含date值的数组上)创建TTL索引。

例如,如果想要在eventlog集合上的lastModifiedDate字段上创建TTL索引,可以在mongo shell 中使用下面的操作:

db.eventlog.createIndex( { "lastModifiedDate": 1 }, { expireAfterSeconds: 3600 } )

操作

过期数据

TTL索引在索引字段值的时间经过特定秒数的时间之后,TTL索引会将文档进行过期操作,例如,过期的阈值是索引字段的值加上特定的秒数。

如果该字段是一个数组,并且在索引中有多个数据值,MongoDB使用数组中的最低(例如,最早的)日期值来计算过期阈值。

如果文档中的索引字段不是一个日期或者包含日期值的数组,文档将不会过期。

如果一个文档不包含索引字段,文档将不会过期。

删除操作

mongod中的后台线程会从索引中读取值,并且会删除集合中过期中的文档。

TTL线程为活跃时,我们将会在db.currentOp()的输出或者数据库分析器收集的数据中查看到删除操作。

####删除操作的时间
当我们在后台创建TTL索引时,TTL线程可以在索引创建的过程中删除文档。如果我们直接在前面构建TTL索引的话,MongoDB会在索引完成构建之后开始删除过期的文档。

TTL索引无法保证过期数据会在过期之后马上被删除。在文档过期时间和MongoDB从数据库中删除文档之间可能会有一段时间的延迟。

删除过期文档的后台进程每60秒运行一次。因此,文档在文档的过期时间段和后台任务运行的时间段之间可能还会保存在集合中。

由于删除操作所花费的时间会根据mongod实例的工作负载而定,过期的数据可能会继续存在超过后台进程运行间隙的60秒时间。

在特定秒数过期文档

如果想要在索引字段的时间过去特定的秒数之后过期数据,我们可以在保存BSON日期值或BSON数据类型对象的数组的字段上创建TTL索引,并且expireAfterSeconds字段指定一个非0值。那么当过去了索引字段中指定的时间加上expireAfterSeconds字段的秒数之后,该文档将会过期。

例如,下面的操作在log_events集合的createAt字段上创建了索引,并且将expireAfterSeconds设置为3600,从而将过期时间设置在createAt指定是时间1个小时之后。

db.log_events.createIndex( { "createdAt": 1 }, { expireAfterSeconds: 3600 } )

当向log_events集合中增加文档时,将createAt字段设置为当前时间:

db.log_events.insert( {
   "createdAt": new Date(),
   "logEvent": 2,
   "logMessage": "Success!"
} )

MongoDB将会自动从log_events集合中删除那些createAt值小于expireAfterSeconds指定秒数的文档。

如果字段包含BSON日期类型对象的数组,如果其中至少一个BSON日期类型的对象旧于expireAfterSeconds指定的秒数时,就会过期数据。

###在特定的时钟时间过期文档
如果希望在一个特定的时钟时间过期文档,通过在一个存储BSON日期类型或者BSON日期类型对象数组的字段上创建一个TTL索引,并且将expireAfterSeconds的值设置为0。对于集合中的每个文档,将索引的日期字段设置到相应的文档过期时间。如果索引字段中包含一个过去的日期,MongoDB将会将该文档看做是过期文档。

例如,下面的操作在log_events集合的expireAt字段创建了一个索引,并且将expireAfterSeconds的值设置为0

db.log_events.createIndex( { "expireAt": 1 }, { expireAfterSeconds: 0 } )

对于每个文档,将expireAt的值设置为文档应该过期的相应时间。例如,下面的insert()操作增加了一个应该在July 22, 2013 14:00:00过期的文档。

db.log_events.insert( {
   "expireAt": new Date('July 22, 2013 14:00:00'),
   "logEvent": 2,
   "logMessage": "Success!"
} )

MongoDB将会自动从log_events集合中删除那些expireAt值早于expireAfterSeconds值的文档,例如,在本案例是为0。这样的话,数据在指定的expireAt值时间点过期。

复制集

在复制集成员上,TTL后台进程会在成员是主节点的状态下删除文档。当成员在从节点的状态下时,TTL后台线程时空闲的。从节点成员会从主节点复制删除操作。

###对查询的支持
TTL索引支持和非TTL索引的相同方式进行查询。

###MMAPv1上的记录分配
使用MMAPv1存储引起时,使用TTL索引的集合自动启用usePowerOf2Sizes。我们不能修改该集合的设置。由于启用了usePowerOf2Sizes,MongoDB必须分配比数据大小更多的磁盘空间。该方法帮助减轻了由于频繁删除操作而单只的存储分区的可能性,促进了更加可预测的存储使用模式。

限制

  • TTL索引是单字段索引,复合索引不支持TTL并且会忽略expireAfterSeconds选项。
  • _id字段不支持TTL索引。
  • 我们不能再一个限制集合中创建TTL索引,因为MongoDB无法删除限制集合中的文档。
  • 我们无法使用createIndex()来修改一个已有索引的expireAfterSeconds值。应该使用collMod数据库命令以及index集合的标记。否则的话,如果需要修改已有索引熏香的值,我们必须先删除该索引,然后重建。
  • 如果已经在某个字段上创建了一个非TTL单字段索引,我们无法在相同的字段上创建TTL索引,因为我们无法创建有相同键值声明的索引,只通过选项进行区分。如果要修改一个非TTL单键值索引为一个TTL索引,我们必须显删除该索引,然后使用expireAfterSeconds选项重建索引。
打赏
微信扫一扫支付
微信logo微信扫一扫, 打赏作者吧~

mickey

记录生活,写给几十年后的自己。