·文档是Mongodb中数据的基本单元,非常类似于关系型数据库管理系统中的行,但更具有表现力。
·集合(collection)可以看作是一个拥有动态模式(dynamic schema)的表。
·Mongodb的一个实例可以拥有多个相互独立的数据库(database),每个数据库都拥有自己的集合。
·每一个文档都拥有一个特殊的键“_id”,这个键在文档所属的集合中是唯一的。
·Mongodb自带了一个简单但功能强大的JavaScript Shell,可以用于管理Mongodb的实例或数据库操作。
1. 文档
文档是Mongodb的核心概念。文档就是键值对的一个有序集。
文档的键是字符串,除了少数例外情况,键可以使用任意UTF-8字符。
键不能含有\0(空字符)。这个字符用于表示键的结尾。
.和$具有特殊意义,只能在特定环境下使用。
Mongodb文档不能有重复的键。
2.集合
集合就是一组文档。
集合是动态模式的。这意味着一个集合里面的文档可以是各式各样的。
思考:因为集合里面可以放置任何文档,那么有必要使用多个集合吗?
·如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是对管理员来说都将是噩梦。
·在一个集合里查询特定类型的文档在速度上也很不划算,分开查询多个集合要快得多。
·把同种类型的文档放在一个集合里,数据会更加集中。
·创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)。索引是按照集合来定义的。在一个集合中只放入一种类型的文档,可以更有效的对集合进行索引。
集合命名:
·集合名不能是空字符串("")。
·集合名不能包含\0字符(空字符)。这个字符表示集合名的结束。
·集合名不能以“system.”开头,这是为系统集合保留的前缀。
·创建的集合不能包含保留字符“$”。
子集合:组织集合的一种惯例是使用"."分隔不同命名空间的子集合。(例如:blog.posts、blog.authors)
3.数据库
在Mongodb中,多个文档组成集合,而多个集合可以组成数据库。
数据库通过名称来标识,这点与集合类似。
·不能是空字符串。
·不得含有(/ \ . " < > : | > $ \0 …)。基本上只能使用ASCII中的字母和数字。
·数据库名区分大小写。
·数据库名最多为64字节。
保留数据库名:admin、local、config
命名空间(namespace):是把数据库名添加到集合名前,得到的集合完全限定名。(如:cms数据库的blog.posts集合,这个集合的命名空间是cms.blog.posts。)
命名空间的长度不得超过121字节,且在实际使用中应小于100字节。
4.启动数据库
默认情况下,Mongodb监听27017端口。mongod还会启动一个非常基本的HTTP服务器,监听数字比主端口号高1000的端口,默认是28017端口。
5.Mongodb Shell简介
Mongodb自带JavaScript Shell,可在Shell中使用命令行与Mongodb实例交互。它是一个独立的Mongodb客户端,通过运行mongo启动Shell时,会连接到Mongodb数据库服务器的test数据库。
使用db命令可以查看当前所在数据库!
在Shell中查看或操作数据会用到4个基本操作:创建、读取、更新和删除(CRUD操作)。
创建:insert函数
可以将一个文档添加到集合中。
下图为存储博客文章的例子,首先创建一个名为post的局部变量,这是一个JavaScript对象,用于表示我们的文档。
然后可以用insert方法将其保存到blog集合中。
读取:find和findOne方法
可以用于查询集合里的文档。若只查询一个文档用findOne,使用find时,Shell会自动显示最多20个匹配的文档,也可以获取更多。
更新:update
接受(至少)两个参数:第一个是限定条件(用于匹配待更新的文档),第二个是新的文档。
假设我们要为先前写的文章增加评论功能,就需要增加一个新的键,用于保存评论数组。
首先修改变量post,增加"comments"键,然后执行update操作,用新版本的文档替换标题为"My blog post"的文章:
删除:remove
使用remove方法可以将文档从数据库中永久删除。如果没有使用任何参数,它会将集合内的所有文档全部删除。它可以接受一个作为限定条件的文档作为参数。例如下面的命令删除上面创建的文档:
>db.blog.remove({title : "My blog post"})
6.数据类型
基本数据类型:
在概念上,Mongodb的文档与JavaScript中的对象相近,因而可认为它类似于JSON。
·null:用于表示空值或者不存在的字段。 {"x" : null}
·布尔型:有两个值true和false。 {"x" : true}
·数值:Shell默认使用64位浮点型数值。因此,以下数值在Shell中是很“正常”的: {"x" : 3.14} 或 {"x" : 3}
对于整型值,可使用NumberInt类(表示4字节带符号整数)或NumberLong类(表示8字符带符号整数),分别举例如下:
{"x" : NumberInt("3")} {"x" : NumberLong("3")}
·字符串:UTF-8字符串都可表示为字符串类型的数据。 {"x" : "foobar"}
·日期:被存储为自新纪元以来经过的毫秒数,不存储时区。 {"x" : new Date()}
·正则表达式:查询时,使用正则表达式作为限定条件,语法与JavaScript的正则表达式语法相同。 {"x" : /foobar/i}
·数组:数据列表或数据集可以表示为数组。 { "x" : ["a","b","c"]}
·内嵌文档:文档可嵌套其它文档,被嵌套的文档作为父文档的值。 {"x" : {"foo" : "bar"}}
·对象id:是一个12字节的ID,是文档的唯一标识。 {"x" : ObjectId()}
_id和ObjectId:
MongoDB 中存储的文档必须有一个"_id" 键。这个键的值可以是任何类型的,默认是个ObjectId 对象。在一个集合里面,每个文档都有唯一的"_id" 值,来确保集合里面每个文档都能被唯一标识。如果有两个集合的话,两个集合可以都有一个值为123 的"_id" 键,但是每个集合里面只能有一个"_id" 是123 的文档。
①.ObjectId
ObjectId 是"_id" 的默认类型。它设计成轻量型的,不同的机器都能用全局唯一的同种方法方便地生成它。这是MongoDB 采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个服务器上同步自动增加主键值既费力还费时。MongoDB 从一开始就设计用来作为分布式数据库,处理多个节点是一个核心要求。后面会看到ObjectId 类型在分片环境中要容易生成得多。
ObjectId 使用12 字节的存储空间,每个字节两位十六进制数字,是一个24 位的字符串。由于看起来很长,不少人会觉得难以处理。但关键是要知道这个长长的ObjectId 是实际存储数据的两倍长。
如果快速连续创建多个ObjectId,会发现每次只有最后几位数字有变化。另外,中间的几位数字也会变化(要是在创建的过程中停顿几秒钟)。这是ObjectId 的创建方式导致的。12 字节按照如下方式生成:
前4 个字节是从标准纪元开始的时间戳,单位为秒。这会带来一些有用的属性。
·时间戳,与随后的. 5 个字节组合起来,提供了秒级别的唯一性。
·由于时间戳在前,这意味着ObjectId 大致会按照插入的顺序排列。这对于某些方面很有用,如将其作为索引提高效率,但是这个是没有保证的,仅仅是“大致”。
·这4 个字节也隐含了文档创建的时间。绝大多数驱动都会公开一个方法从ObjectId 获取这个信息。
因为使用的是当前时间,很多用户担心要对服务器进行时间同步。其实没有这个必要,因为时间戳的实际值并不重要,只要其总是不停增加就好了(每秒一次)。
接下来的3 字节是所在主机的唯一标识符。通常是机器主机名的散列值。这样就可以确保不同主机生成不同的ObjectId,不产生冲突。
为了确保在同一台机器上并发的多个进程产生的ObjectId 是唯一的,接下来的两字节来自产生ObjectId 的进程标识符(PID)。
前9 字节保证了同一秒钟不同机器不同进程产生的ObjectId 是唯一的。后3 字节就是一个自动增加的计数器,确保相同进程同一秒产生的ObjectId 也是不一样的。同一秒钟最多允许每个进程拥有2563(16 777 216)个不同的ObjectId。
②.自动生成_id
前面讲到,如果插入文档的时候没有"_id" 键,系统会自动帮你创建一个。可以由MongoDB 服务器来做这件事情,但通常会在客户端由驱动程序完成。理由如下。
虽然ObjectId 设计成轻量型的,易于生成,但是毕竟生成的时候还是产生开销。在客户端生成体现了MongoDB 的设计理念:能从服务器端转移到驱动程序来做的事,就尽量转移。这种理念背后的原因是,即便是像MongoDB 这样的可扩展数据库,扩展应用层也要比扩展数据库层容易得多。将事务交由客户端来处理,就减轻了数据库扩展的负担。
在客户端生成ObjectId,驱动程序能够提供更加丰富的API。例如,驱动程序可以有自己的insert 方法,可以返回生成的ObjectId,也可以直接将其插入文档。如果驱动程序允许服务器生成ObjectId,那么将需要单独的查询,以确定插入的文档中的"_id" 值。
PS:内容整理于《Mongodb权威指南》