MongoDB中的“普通”集合是动态创建的,而且可以自动增长以容纳更多的数据。MongoDB中还有另一种不同类型的集合,叫做固定集合,固定集合需要事先创建好,而且它的大小是固定的( 如图6-1所示)。说到固定大小的集合,有一个很有趣的问题:向-一个已经满了的固定集合中插入数据会怎么样?答案是,固定集合的行为类似于循环队列。如果已经没有空间了,最老的文档会被删除以释放空间,新插入的文档会占据这块空间。也就是说,当固定集合被占满时,如果再插入新文档,固定集合会自动将最老的文档从集合中删除。
固定集合的访问模式与MongoDB中的大部分集合不同:数据被顺序写入磁盘上的固定空间。因此它们在碟式磁盘(spinning disk)。上的写入速度非常快, 尤其是集合拥有专用磁盘时(这样就不会因为其他集合的一些随机性的写操作而“中断”)。固定集合可以用于记录日志,尽管它们不够灵活。虽然可以在创建时指定集合大小,但无法控制什么时候数据会被覆盖。
不同于普通集合,固定集合必须在使用之前先显示创建。可以使用createCollection函数:
创建了一个1024字节,最大文档数为10的固定集合。固定集合创建之后,就不能改变了(如果需要修改固定集合的属性,只能将它删除之后再重建)。因此,在创建大的固定集合之前应该仔细想清楚它的大小。
注意:为固定集合指定文档数量限制时,必须同时指定固定集合的大小。不管先达到哪-一个限制,之后插入的新文档就会把最老的文档挤出集合:固定集合的文档数量不能超过文档数量限制,固定集合的大小也不能超过大小限制。
创建固定集合时还有另一个选项,可以将已有的某个常规集合转换为固定集合,可以使用convertToCapped命令实现。下 面的例子将test集合转换为一个大小为10 000字节的固定集合:
db.runCommand(
{"converToCapped": "test", "size":1000}
)
对固定集合可以进行一种特殊的排序,称为自然排序。自然排序返回结果集中文档的顺序就是文档在磁盘上的顺序,也就是文档的插入顺序:
循环游标(tailablecursor)是一种特殊的游标,当循环游标的结果集被取光后,游标不会被关闭。循环游标的灵感来自tail -f 命令(循环游标跟这个命令有点儿相似),会尽可能久地持续提取输出结果。由于循环游标在结果集取光之后不会被关闭,因此,当有新文档插入到集合中时,循环游标会继续取到结果。由于普通集合并不维护文档的插入顺序,所以循环游标只能用在固定集合上。
循环游标通常用于当文档被插入到“工作队列”(其实就是个固定集合)时对新插入的文档进行处理。如果超过10分钟没有新的结果,循环游标就会被释放,因此,当游标被关闭时自动重新执行查询是非常重要的。
注意:不能在mongoshell中使用循环游标
默认情况下,每个集合都有一个“id"索引。但是,如果在调用createCollection创建集合时指定autoIndexId选项为false,创建集合时就不会自动在" id"上创建索引。实践中不建议这么使用,但是对于只有插入操作的集合来说,这确实可以带来速度的稍许提升。
对于固定集合中的内容何时被覆盖,如果需要更加灵活的老化移出系统(ageoutsystem),可以使用TTL索引(time-to-live index,具有生命周期的索引),这种索引允许为每一个文档设置一个超时时间。一个文档到达预设置的老化程度之后就会被删除。这种类型的索引对于缓存问题(比如会话的保存)非常有用。
在createIndex中指定expireAgterSeconds选项就可以创建一个TTL索引:
这样就在"lastUpdated"字段上建立了一个TTL索引。如果一个文档的"lastUpdated"字段存在并且它的值是日期类型,当服务器时间比文档的"las tUpdated"字段的时间晚expireAfterSeconds秒时,文档就会被删除。
为了防止活跃的会话被删除,可以在会话上有活动发生时将"lastUpdated"字段的值更新为当前时间。只要"lastUpdated"的时间距离当前时间达到10分钟,相应的文档就会被删除。
MongoDB每分钟对TTL索引进行一次清理,所以不应该依赖以秒为单位的时间保证索引的存活状态。可以使用collMod命令修改expireAfterSeconds的值:
db.runCommand({collMod: "someapp.cache", "expireAfterSeconds": 20 * 60})
在一个给定的集合上可以有多个TTL索引。TTL索引不能是复合索引,但是可以像普通索引一样用来优化排序和查询。
MongoDB支持几种类型的地理空间索引。其中最常用的是2dsphere索引(用于地球表面类型的地图)和2d索引(用于平面地图和时间连续的数据)。
2dsphere允许使用GeoJSON格式(http://www.geojson.org) 指定点、线和多边形。点可以用形如[longitude,latitude] ([ 经度,纬度])的两个元素的数组表示:
{
"name":"New York City",
"loc": {
"type":"Point",
"coordinates": [50, 2]
}
}
线可以用一个又点组成的数组来表示:
{
"name":"Hudson River",
"loc": {
"type":"Line",
"coordinates": [[0,1], [0,2], [1,2]]
}
}
多边形的标识方式与线一样(都是一个由点组成的数组),但是type不同:
{
"name":"New England",
"loc": {
"type":"Polygon",
"coordinates": [[0,1], [0,2], [1,2]]
}
}
loc字段的名字可以是任意的,但是其中的子对象由GeoJSON指定的,不能改变。
在createIndex中使用2dsphere
选项就可以创建一个地理空间索引:
db.world.createIndex({"loc":"2dsphere"})