MongoDB 固定集合(Capped Collections)

MongoDB 固定集合(Capped Collections)是性能出色且有着固定大小的集合,对于大小固定,我们可以想象其就像一个环形队列,当集合空间用完后,再插入的元素就会覆盖最初始的头部的元素!


创建固定集合

我们通过createCollection来创建一个固定集合,且capped选项设置为true:


>db.createCollection("cappedLogCollection",{capped:true,size:10000})

 

还可以指定文档个数,加上max:1000属性:


>db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})

 

判断集合是否为固定集合:


>db.cappedLogCollection.isCapped()


 

如果需要将已存在的集合转换为固定集合可以使用以下命令:


>db.runCommand({"convertToCapped":"posts",size:10000})


 

以上代码将我们已存在的 posts 集合转换为固定集合。


固定集合查询

固定集合文档按照插入顺序储存的,默认情况下查询就是按照插入顺序返回的,也可以使用$natural调整返回顺序。



>db.cappedLogCollection.find().sort({$natural:-1})


 


固定集合的功能特点

可以插入及更新,但更新不能超出collection的大小,否则更新失败,不允许删除,但是可以调用drop()删除集合中的所有行,但是drop后需要显式地重建集合。

在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。


固定集合属性及用法

属性

  • 属性1:对固定集合进行插入速度极快
  • 属性2:按照插入顺序的查询输出速度极快
  • 属性3:能够在插入最新数据时,淘汰最早的数据

用法

  • 用法1:储存日志信息
  • 用法2:缓存一些少量的文档

MongoDB GridFS

GridFS 用于存储和恢复那些超过16M(BSON文件限制)的文件(如:图片、音频、视频等)。

GridFS 也是文件存储的一种方式,但是它是存储在MonoDB的集合中。

GridFS 可以更好的存储大于16M的文件。

GridFS 会将大文件对象分割成多个小的chunk(文件片段),一般为256k/个,每个chunk将作为MongoDB的一个文档(document)被存储在chunks集合中。

GridFS 用两个集合来存储一个文件:fs.files与fs.chunks。

每个文件的实际内容被存在chunks(二进制数据)中,和文件有关的meta数据(filename,content_type,还有用户自定义的属性)将会被存在files集合中。

以下是简单的 fs.files 集合文档:


{
   "filename": "test.txt",
   "chunkSize": NumberInt(261120),
   "uploadDate": ISODate("2014-04-13T11:32:33.557Z"),
   "md5": "7b762939321e146569b07f72c62cca4f",
   "length": NumberInt(646)
}


 

以下是简单的 fs.chunks 集合文档:



{
   "files_id": ObjectId("534a75d19f54bfec8a2fe44b"),
   "n": NumberInt(0),
   "data": "Mongo Binary Data"
}



GridFS 添加文件

现在我们使用 GridFS 的 put 命令来存储 mp3 文件。 调用 MongoDB 安装目录下bin的 mongofiles.exe工具。

打开命令提示符,进入到MongoDB的安装目录的bin目录中,找到mongofiles.exe,并输入下面的代码:



>mongofiles.exe -d gridfs put song.mp3



 

GridFS 是存储文件的数据名称。如果不存在该数据库,MongoDB会自动创建。Song.mp3 是音频文件名。

开始使用GridFS:mongofiles

Mongofiles内置在MongoDB发布版中,可以用来在GridFS中上传,下载,列示,查找或删除文件。像执行其他命令行工具一样,执行mongofiles —help可以查看可用选项。

现在我们向GridFS上传文件,列出文件和下载文件。



$ echo "hello,word" > foo.txt
$ ./mongofiles put foo.txt #上传文件
$ ./mongofiles list #列出文件
$  rm foo.txt #删除本地文件
$ ./mongofiles get foo.txt #下载文件
$ cat foo.txt
$ ./mongofiles search foo.txt #查询文件
$ ./mongofiles delete foo.txt #删除Grid上的文件


 

如果你设置了用户权限, 或者出现类似 assertion: 16428 Error storing GridFS chunk for file: foo.txt, error: { connectionId: 32, err: "not authorized for insert on test.fs.chunks", code: 13, n: 0, ok: 1.0 } 的错误时,你需要添加更多参数,例如:mongofiles -u username -p password -d database  put foo.txt 更多参数查看这里,或者直接用 mongofiles --help

使用以下命令来查看数据库中文件的文档:



>db.fs.files.find()


 

以上命令执行后返回以下文档数据:


{
   _id: ObjectId('534a811bf8b4aa4d33fdf94d'), 
   filename: "song.mp3", 
   chunkSize: 261120, 
   uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
   length: 10401959 
}


 

我们可以看到 fs.chunks 集合中所有的区块,以下我们得到了文件的 _id 值,我们可以根据这个 _id 获取区块(chunk)的数据:



>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})


 

以上实例中,查询返回了 40 个文档的数据,意味着mp3文件被存储在40个区块中。



MongoDB 自动增长

MongoDB 没有像 SQL 一样有自动增长的功能, MongoDB 的 _id 是系统自动生成的12字节唯一标识。

但在某些情况下,我们可能需要实现 ObjectId 自动增长功能。

由于 MongoDB 没有实现这个功能,我们可以通过编程的方式来实现,以下我们将在 counters 集合中实现_id字段自动增长。


使用 counters 集合

考虑以下 products 文档。我们希望 _id 字段实现 从 1,2,3,4 到 n 的自动增长功能。



{
  "_id":1,
  "product_name": "Apple iPhone",
  "category": "mobiles"
}


 

为此,创建 counters 集合,序列字段值可以实现自动长:



>db.createCollection("counters")



 

现在我们向 counters 集合中插入以下文档,使用 productid 作为 key:



{
  "_id":"productid",
  "sequence_value": 0
}



 

sequence_value 字段是序列通过自动增长后的一个值。

使用以下命令插入 counters 集合的序列文档中:


>db.counters.insert({_id:"productid",sequence_value:0})

创建 Javascript 函数

现在,我们创建函数 getNextSequenceValue 来作为序列名的输入, 指定的序列会自动增长 1 并返回最新序列值。在本文的实例中序列名为 productid 。



>function getNextSequenceValue(sequenceName){
   var sequenceDocument = db.counters.findAndModify(
      {
         query:{_id: sequenceName },
         update: {$inc:{sequence_value:1}},
         new:true
      });
   return sequenceDocument.sequence_value;
}



使用 Javascript 函数

接下来我们将使用 getNextSequenceValue 函数创建一个新的文档, 并设置文档 _id 自动为返回的序列值:


>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Apple iPhone",
   "category":"mobiles"})

>db.products.insert({
   "_id":getNextSequenceValue("productid"),
   "product_name":"Samsung S3",
   "category":"mobiles"})

 

就如你所看到的,我们使用 getNextSequenceValue 函数来设置 _id 字段。

为了验证函数是否有效,我们可以使用以下命令读取文档:



>db.prodcuts.find()



 

以上命令将返回以下结果,我们发现 _id 字段是自增长的:



{ "_id" : 1, "product_name" : "Apple iPhone", "category" : "mobiles"}
{ "_id" : 2, "product_name" : "Samsung S3", "category" : "mobiles" }



MongoDB 索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构


ensureIndex() 方法

MongoDB使用 ensureIndex() 方法来创建索引。

语法

ensureIndex()方法基本语法格式如下所示:



>db.COLLECTION_NAME.ensureIndex({KEY:1})


 

语法中 Key 值为你要创建的索引字段,1为指定按升序创建索引,如果你想按降序来创建索引指定为-1即可。

实例


>db.col.ensureIndex({"title":1}) >


 

ensureIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。



>db.col.ensureIndex({"title":1,"description":-1}) >



 

ensureIndex() 接收可选参数,可选参数列表如下:

Parameter

Type

Description

background

Boolean

建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 "background" 可选参数。 "background" 默认值为false

unique

Boolean

建立的索引是否唯一。指定为true创建唯一索引。默认值为false.

name

string

索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。

dropDups

Boolean

在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.

sparse

Boolean

对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.

expireAfterSeconds

integer

指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。

v

index version

索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。

weights

document

索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。

default_language

string

对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语

language_override

string

对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.

实例

在后台创建索引:



db.values.ensureIndex({open: 1, close: 1}, {background: true})

通过在创建索引时加background:true 的选项,让创建工作在后台执行



MongoDB 高级索引

考虑以下文档集合(users ):


{
   "address": {
      "city": "Los Angeles",
      "state": "California",
      "pincode": "123"
   },
   "tags": [
      "music",
      "cricket",
      "blogs"
   ],
   "name": "Tom Benzamin"
}

 

以上文档包含了 address 子文档和 tags 数组。


索引数组字段

假设我们基于标签来检索用户,为此我们需要对集合中的数组 tags 建立索引。

在数组中创建索引,需要对数组中的每个字段依次建立索引。所以在我们为数组 tags 创建索引时,会为 music、cricket、blogs三个值建立单独的索引。

使用以下命令创建数组索引:



>db.users.ensureIndex({"tags":1})



 

创建索引后,我们可以这样检索集合的 tags 字段:


>db.users.find({tags:"cricket"})


 

为了验证我们使用使用了索引,可以使用 explain 命令:



>db.users.find({tags:"cricket"}).explain()


 

以上命令执行结果中会显示 "cursor" : "BtreeCursor tags_1" ,则表示已经使用了索引。


索引子文档字段

假设我们需要通过city、state、pincode字段来检索文档,由于这些字段是子文档的字段,所以我们需要对子文档建立索引。

为子文档的三个字段创建索引,命令如下:


>db.users.ensureIndex({"address.city":1,"address.state":1,"address.pincode":1})

 

一旦创建索引,我们可以使用子文档的字段来检索数据:


>db.users.find({"address.city":"Los Angeles"})


 

记住查询表达式必须遵循指定的索引的顺序。所以上面创建的索引将支持以下查询:



>db.users.find({"address.city":"Los Angeles","address.state":"California"})


 

同样支持以下查询:

>db.users.find({"address.city":"LosAngeles","address.state":"California","address.pincode":"123"})