MongoDB笔记 -- 高级操作

Posted by 无限可能的想象力 on March 20, 2017

数据库命令

MongoDB除了支持创建、读取、更新、删除文档。还支持大量的高级操作。这些操作都是用命令实现的。

命令的工作原理

MongoDB中的命令其实是作为一种特殊类型的查询来实现的,这些查询对$cmd集合来执行,runCommand仅仅是接受命令文档,执行等价查询,

当MongoDB服务器得到查询$cmd集合的请求时,会启动一套特殊的逻辑来处理,而不是交给普通的查询代码来执行。几乎所有MongoDB驱动程序都提供一个类似于runCommand的帮助方法来执行命令,但是如果有必要,总是可以使用一个简单查询的方式来运行命令。

固定集合

MongoDB还支持另外一种集合——固定集合,要事先创建,而且大小固定。 固定集合很像环形队列,如果空间不足,最早的文档就会被删除,为新的文档腾出空间。这意味着固定集合在新文档插入的时候自动淘汰新的文档。

固定集合和普通集合还有一个区别,就是在默认情况下固定集合没有索引,即便是_id上也没有索引。

属性及用法

固定集合的功能与限制合二为一,有一些有趣的特性。

  1. 对固定集合进行插入速度极快。做插入操作时,无须额外分配空间。

  2. 按照插入顺序输出的查询速度极快。因为文档本身就是按照插入顺序存储的。

  3. 固定集合能够在新数据插入时,自动淘汰最早的数据。

插入快速,按照插入顺序查询也快速、自动淘汰,这几样组合起来使得固定集合特别适合像日志这种应用场景。

事实上,MongoDB中设计规定集合的目的就是用来讯处内部复制日志oplog。固定集合还有一个很好的用法,就是缓存少量的文档。

一般来说,固定集合适用于任何想要自动淘汰过期属性的场景,没有太多的操作限制。

创建固定集合

固定集合必须在使用前显式地创建。使用create命令创建。在shell中,可以使用createCollection来创建:

> db.createCollection("my_collection", {capped: true, size: 100000})
{ "ok" : 1 }
> db.createCollection("my_collection", {capped: true, size: 100000, max:100})

还可以通过转换已有的普通集合的方式来创建固定集合。使用convertToCapped命令来完成这个操作。

> db.runCommand({convertToCapped: "foo", size:10000, max:100})
{ "ok" : 1 }

自然排序

固定集合中有种特殊的排序方式,叫做自然排序。自然排序就是文档在磁盘上的顺序。

因为固定集合的文档总是按照插入的顺序存储的,自然顺序就是与此相同的。也可以使用自然顺序按照反向插入的顺序查询。

尾部游标

尾部游标是一种特殊的持久游标,这类游标不会在没有结果后销毁。游标受到tail -f命令的启发,类似地会尽可能持续地获取结果。因为这类游标在没有结果后也不销毁,所以一旦有新文档添加到集合里面就会被取回并输出。尾部游标只能用在固定集合上。

可惜Mongo shell并不支持尾部游标。

游标没有销毁,要么处理结果,要么等着有更多的结果。

GridFS:储存文件

GridFS是一种在MongoDB中存储大二进制文件的机制。使用GridFS存储文件有以下几个原因:

  • 利用GridFS可以简化需求。

  • GridFS会直接利用已建立的复制或者分片机制,所以对于文件储存来说故障恢复和扩展都很容易。

  • GridFS可以避免用于储存用户上传内容的文件系统出现的某些问题。

  • GridFS不产生磁盘碎片,因为MongoDB分配数据文件空间时以2GB为一块。

内部原理

GridFS是一个建立在普通MongoDB文档基础上的轻量级文件储存规范。

GridFS的一个基本思想就是可以将大文件分成很多块,每块作为一个单独的文档储存,这样就能存大文件了。由于MongoDB支持在文档中存储二进制数据,可以最大限度减少块的存储开销。另外,除了存储文件本身的块,还有一个单独的文档用来存储分块的信息和文件的元数据。

GridFS的块有一个单独的集合。默认情况下,块将使用fs.chunks集合,如有需要可以覆盖。

文件的元数据放在另一个集合中,默认是fs.files。这里面的每个文档代表GridFS中的一个文件,与文件相关的自定义元数据也可以存在其中,

服务器端脚本

在服务器端可以通过db.eval函数来执行JavaSript脚本。也可以把JavaScript脚本保存在数据库中,然后在别的数据库命令中调用。

db.eval

利用db.eval可以在MongoDB的服务器端执行任意的JavaScript脚本。这个函数先将给定的JavaScript字符串传送给MongoDB(在这里执行),然后返回结果。

db.eval可以用来模拟多文档事务:db.eval锁住数据库,然后执行JavaScript,再解锁。虽然没有回滚机制,但这的确能保证一系列操作按照指定顺序发生(除非出错)。

发送代码有两个选择:或者封装进一个函数,或者不封装。

> db.eval("function() {return 1;}")
WARNING: db.eval is deprecated
1
> db.eval("function() {return 1;}")
WARNING: db.eval is deprecated
1

只有传递参数的时候,才必须要封装成一个函数。参数通过db.eval的第二个参数传递。要写成一个数组的形式。

db.eval的表达式要是复杂的话,调试起来就需要些技巧了。调试的一个好方法就是将调试信息写进数据库日志中,这个可以通过print函数来完成。

存储JavaScript

每个MongoDB的数据库中都有个特殊的集合,叫做system.js,用来存放JavaScript变量。这些变量可以在任何MongoDB的JavaScript上下文中调用,包括$where子句,db.eval调用,MapReduce作业。用insert就可以讲变量加进system.js中。

使用存储的JavaScript缺点就是代码会与常规的袁大妈控制脱离,会搅乱客户端发送来的JavaScript。

最适合使用存储的JavaScript的情况就是程序中有多个地方都要用到一个JavaScript函数。将这样的函数放置在中心位置,要是有更新的话就可以不必每处都修改。

安全性

执行JavaScript代码,就必须要谨慎考虑MongoDB的安全性。使用不慎,就会发生类似于关系型数据库的注入式攻击。

数据库引用

数据库引用,也叫做DBRefDBRef就行URL,唯一确定一个到文档的引用。它自动加载文档的 方式正如网站中URL通过连接自动加载Web页面一样。

什么是DBRef

DBRef是一个内嵌文档,就像MongoDB的其他内嵌文档一样。但是DBRef有一些必需键。

DBRef指向一个集合,还有一个id_value用来在集合里面根据_id确定唯一的文档。这来那个IT哦啊信息使得DBRef能唯一表示MongoDB数据库 内的任何一个文档。

DBRef中的键的书序不能改变。第一个必须是$ref,接着是$id,然后是(可选的)$db