MongoDB的基本概念
文档是MongoDB中数据的基本单元,非常类似于关系型数据库管理中的行,但更具表现力。
集合可以看作是一个拥有动态模式的表。
MongoDB的一个实例可以拥有相互独立的数据库,每个数据库都拥有自己的集合。
每个文档都有一个特殊的键
_id
,这个键在文档所属的集合中是唯一的。MongoDB自带了一个简单但功能强大的JavaScript shell,可用于管理MongoDB的实例或者数据操作。
文档
文档是MongoDB的核心概念。文档就是键值对的一个有序集。例如在 JavaScript 里面,文档就被表示为对象:
{"greeting": "Hello,World!"}
但是大多数的文档会被这个简单的例子复杂的多,通常会表现为多个键/值对:
{"greeting": "Hello,World!", "foo": 3}
从上面的例子可以看书,文档值可以是多种不同的数据类型。
文档的键是字符串,除了少数的例外情况。键可以使用任意 UTF-8 字符。
键不能含有
\0
(空字符),这个字符用于表示键的结尾。.
和$
具有特殊的意义,只能在特定的环境下使用。
而且MongoDB不但区分类型,而且区分大小写,下面的每组的两个文档都是不同的:
{"foo": 3}
{"foo": "3"}
{"foo": 3}
{"Foo": 3}
还有一个重要的事情需要注意,就是MongoDB的文档不能有重复的键,例如下面的就是违法的:
{"greenting": "Hello, World!", "greenting": "Hello, MongoDB!"}
集合
动态模式
集合是动态模式的,所以任何文档都可以存在同一个集合里面:
{"greenting": "Hello, World!"}
{"foo": 5}
既然集合里面可以放置任何文档,随之而来的一个问题就是,还有必要使用多个集合吗:
各种各样的文档放到一个集合里面,对于开发者和管理员来说不方便管理。
在一个集合里面查询特定类型的文档在速度上不划算,分开查询速度则快得多。
把同种类型的集合放到一个集合里面,数据会更加集中。
创建索引时,因为一个集合中只放入一种类型的文档,可以更加有效的对集合进行索引。
命名
集合名需要时满足下列条件的任意UTF-8字符串:
集合名不能是空字符串(””)
集合名不能包含
\0
字符集合名不能以
system.
开头集合名中不能包含保留字符
$
数据库
在MongoDB中,多个文档组成集合,多个集合可以组成数据库。
数据库名必须是满足以下条件的UTF-8字符:
不能是空字符串(“”)
数据库名应该区分大小写,即便是在不区分大小写的系统也是如此。简单起见,数据库名应该全部小写。
数据库名最多应为64字节。
因为数据库最终会变为文件系统里的文件,而数据库名则就是相应的文件名。这也是数据库名有这么多限制的原因。
但是一些数据库的名字是保留的,比如admin
,local
,config
。
启动MongoDB服务器
service mongod start
默认情况下,MongoDB的监听端口默认是27017。
启动Mongo客户端
mongo
但是这个你要确保你的恶服务器已经启动了。这样就会自动进行连接MongoDB服务器了。
这个 shell 工具其实是一个功能完备的 JavaScript 解释器。可以运行任意的 JS 程序。
基础命令
db # 查看当前选择的数据库
show dbs # 查看当前服务器的数据库列表
use footer # 选择数据库
当选定数据库之后,系统会将数据库连接赋值给全局变量db
。这个变量就是通过 shell 访问 MongoDB 的主要入口点。
例如,通过db.baz
可返回当前数据库的baz
集合。
基本操作
创建
post = {"title": "My Blog Post", "content": "Here's my blog post.", "data": new Date()}
db.blog.insert(post)
这个插入的数据是一个 JavaScript 对象。它有这么几个键:title
,content
,date
。在查询的时候,我们会看到有一个额外的_id
,这个会在下面进行解释。
查找
查找多个文档
db.blog.find()
查找单个文档
db.blog.findOne()
find
和findOne
都接受一个查询文档作为限定条件。使用find
的时候,shell会自动显示最多20个匹配的文档。
更新
例如:给先前的数据增加一个comments
键:
post.comment = []
db.blog.update({"title": "My Blog Post"}, post)
update
至少接受两个参数,第一个参数是限定条件,用于匹配要更新的文档。第二个是要更新的文档。
然后我们在db.blog.find()
就可以看到新的键
删除
使用remove()
方法可以将文档从数据库中永久删除。但是如果没有指定任何条件,那么则会删除集合内的所有文档。也可以接受一个限定条件作为参数:
db.blog.remove({"title": "My Blog Post"})
现在集合又是空的了。
_id
和 ObejctId
MongoDB中存储的数据必须有一个_id
键。这个键的值可以是任何类型的。默认是一个 Object 对象。在一个集合里面,每个文档都有一个唯一的_id
,类似MySQL的自增ID。
ObejctId
在设计MongoDB的时候,初衷就是用作分布式数据库,所以能够在分片的环境中生成唯一的标识符就非常的重要。ObejctId
使用12字节的存储空间,是一个由24个十六进制数字组成的字符串。
ObjectId的12字节,按照如下方式生成:
前4个字节是从标准纪元开始的时间戳,单位为秒。因为使用的是当前时间,所以很担心是否要对服务器进行时钟同步。这确实是一个好主意。但是时间戳的实际值并不重要,因为只要它总是不停的增加就好了。
接下来的3个字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样可以确保不同主机生成不同的ObjectId。不产生冲突。
接下来的两个字节来自产生ObjectId的进程的进程标识符(PID)。
前9个字节保证了同一秒钟不同机器不同进程产生的ObjectId是唯一的。最后3字节是一个自动增加的计数器。确保相同进程同一秒产生的ObjectId也是不一样的。
自动生成_id
如果插入文档时没有_id
键,系统会帮你自动创建一个。可以由服务器来做这种事情。但是通过会在客户端由驱动程序完成。这样则减轻了数据库扩展的负担。
使用MongoDB shell
使用下面的方式连接到其他的任何MongoDB实例
mongo host:port/db
这样就连接到host
(IP地址),端口是port
上的db
(此处可以更换为你要连接的数据库)了。
这样启用之后,就不会连接到任何数据库
mongo --nodb
在启动之后,可以使用new Mongo(hostname)
命令来选择想要连接到的Mongod了。
conn = new Mongo(host:port)
db = conn.getDB('db')
执行这些命令之后,就可以像平常一样使用db了。
我们可以通过下面的功能来查看帮助手册
help
如果想要知道一个函数是做什么用的,可以直接在shell中输入函数名,但是不要加括号
db.foo.update
使用shell执行脚本
比如:
mongo script1.js script2.js script3.js
亦或者:
mongo --quiet host:port/db script1.js script2.js script3.js
--quiet
命令则是让mongo不要打印”MongoDB shell version ……”的提示了。
或者在交互式shell中运行脚本
load("script1.js")
然而在脚本中,shell辅助函数则是不能使用的。所以我们需要使用辅助函数对应的JS函数:
辅助函数 | 等价函数 |
---|---|
use foo | db.getSisterDB(“foo”) |
show dbs | db.getMongo().getDBs() |
show collections | db.getCollectionNames() |
创建.mongorc.js
文件
这个文件会在启动 shell 时自动运行。一般来说创建此脚本的作用是创建一些自己需要的全局变量,或者为太长的名字创建比较比较短的别名,或者重写内置函数,最常见则是移除一些比较“危险”的 shell 内置函数。注意这种方法不能保护数据库免受恶意用户的攻击,只能预防自己的手误。启动 mongo时指定--norc
参数,就可以禁止加载.mongorc.js
var no = function(){
print("Not on my watch");
};
db.dropDatabase = DB.prototype.dropDatabase = no; //禁止删除数据库
DBCollection.prototype.drop = no; //禁止删除集合
DBCollection.prototype.dropIndex = no; //禁止删除索引
当需要编辑大块的代码或者对象的时候,我们可以指定编辑器,然后我们可以在编辑器内进行编辑:
// .mongorc.js
EDITOR = "/usr/bin/vim";
比如在 mongo shell 中:
>var wap = db.books.findOne({title: "War and Peace"})
>edit wap
修改完成之后,保存并退出编辑器。变量就会重新解析然后加载回 shell。