MongoDB的一个实例可以拥有多个相互独立的数据库,每个数据库都拥有自己的多个集合,集合中包含多个文档。数据库通过名称来标识。数据库名可以是满足以下条件的任意UTF-8字符串。数据库最终会变成文件系统里的文件,而数据库名就是相应的文件名。

  • 不能是空字符串
  • 不能含有/、\、.、"、*、<、>、:、|、?、$ 、\0。基本上,只能使用ASCII中的字母和数字。
  • 数据库名区分大小写,即便是在不区分大小写的文件系统中也是如此。简单起见,数据库名应全部小写。
  • 数据库名最多为64字节。
    有一些数据库名是保留的,可以直接访问这些有特殊语义的数据库。这些数据库如下:
    admin:从身份验证角度讲,这是root数据库。如果将一个用户添加到admin数据库,这个用户将自动获得所有数据库的权限。再者,一些特定的服务器端命令也只能从admin数据库运行,如列出数据库或关闭服务器。
    local:这个数据库永远都不可以复制,且一台服务器上的所有本地集合都可以存储在这个数据库中。
    config:MongoDB用于分片设置时,分片信息会存储在config数据库中。
    把数据库名添加到集合名前,得到集合的完全限定名,即命名空间。命名空间长度不得超过121字节,且在实际使用中应小于100字节。

文档

文档是MongoDB中数据的基本单元,是键值对的一个有序集,非常类似于关系型数据库管理系统中的行。每个文档都有一个特殊的键"_id",这个键在文档所属的集合中是唯一的。
文档的键是字符串(除了少数例外情况,键可以使用任意UTF-8字符),键不能含有\0(空字符),这个字符用于表示键的结尾,.和$具有特殊意义,只能在特定环境下使用。MongoDB的文档不能有重复的键,且MongoDB区分类型,区分大小写。文档中的值可以是多种不同的数据类型(甚至可以是一个完整的内嵌文档),在下面的例子中,“greeting”的值是一个字符串,而“foo”的值是一个整数。

{"greeting" : "Hello, world", "foo" : 3}

文档中的键/值对是有序的,​​{"x":"1","y":2}​​​与​​{"y":2,"x":1}​​是不同的。通常字段顺序并不重要,无须让数据库模式依赖特定的字段顺序(MongoDB会对字段重新排序)。在某种特殊情况下,字段顺序变得非常重要。

在概念上,MongoDB的文档与JavaScript中的对象相近,因而可认为它类似于JSON。JSON仅包含6种数据类型:null、布尔、数字、字符串、数组和对象。MongoDB在保留JSON基本键/值对特性的基础上,添加了其他一些数据类型。

null: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:对象id是一个12字节的ID,是文档的唯一标识 {“x” : ObjectId()}

二进制数据:二进制数据是一个任意字节的字符串。它不能直接在shell中使用。如果要将非UTF-8字符保存到数据库中,二进制数据是唯一的方式。

代码:查询和文档中可包含任意JavaScript代码 {“x” : function() { /* … */}}
有几种大多数情况下仅在内部使用(或被其他类型取代)的类型。

在JavaScript中,Date类可以用作MongoDB的日期类型。创建日期对象时,应使用new Date(…),而非Date(…)。如将构造函数作为函数进行调用(即不包括new的方式),返回的是日期的字符串表示,而非日期(Date)对象。这个结构与MongoDB无关,是JavaScript的工作机制决定的。如果没有注意到这一点,没有始终使用日期(Date)构造函数,将得到一堆混乱的日期对象和日期的字符串。由于日期和字符串之间无法匹配,所以执行删除、更新及查询等几乎所有操作时会导致很多问题。
shell根据本地时区设置显示日期对象,但是数据库中存储的日期为新纪元以来的毫秒数,并未存储对应的时区。

数组是一组值,它既能作为有序对象(如列表、栈或队列),也能作为无序对象(如数据集)来操作。比如​​{"things" : ["pie", 3.14]}​​​,“things”这个键的值是一个数组,数组可包含不同数据类型的元素(在此,是一个字符串和一个浮点数)。实际上,常规的键/值对支持的所有值都可以作为数组的值,数组中甚至可以嵌套数组。
文档中的数组能够知道如何深入数组内部对其内容进行操作。这样就能使用数组内容对数组进行查询和构建索引了。例如,MongoDB可以查询出"things"数组中包含3.14这个元素的所有文档。要是经常使用这个查询,可以对“things”创建索引来提高性能。MongoDB可以使用原子更新对数组内容进行修改,比如深入数组内部将pie改为pi。

内嵌文档是文档作为键的值。例如,一个文档表示一个人,同时还要保存地址等信息。“address”键的值是一个内嵌文档,这个文档有自己的"street"、“city”和"state"键以及对应的值。同数组一样,MongoDB能够理解内嵌文档的结构,并能深入其中构建索引、执行查询或者更新。

{
"name" : "John Doe",
"address" : {
"street" : "123 Park Street",
"city" : "Anytown",
"state" : "NY"
}
}

MongoDB这样做的坏处就是会导致更多的数据重复。假设"address"是关系数据库中的一个独立的表,需要修正地址中的拼写错误。当我们对"people"和"address"执行连接操作时,使用这个地址的每个人的信息都会得到更新。但是在MongoDB中,则需要对每个人的文档分别修正拼写错误。

MongoDB中存储的文档必须有一个"_id"键,这个键的值可以是任何类型的,默认是个ObjectId对象。在一个集合里面,每个文档都有唯一的"_id",确保集合里面每个文档都能被唯一标识。如果有两个集合的话,两个集合可以都有一个"_id”的值为123,但是每个集合里面只能有一个文档的“_id”值为123。

ObjectId是"_id"的默认类型。它设计成轻量型的,不同的机器都能用全局唯一的同样方法方便地生成它。这是MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个服务器上同步自动增加主键值既费力又费时。因为设计MongoDB的初衷就是用作分布式数据库,所以能够在分片环境中生成唯一的标识符非常重要。ObjectId使用12字节的存储空间,是一个由24个十六进制数字组成的字符串(每个字节可以存储两个十六进制数字)。如果快速连续创建多个ObjectId,会发现每次只有最后几位数字有变化。另外中间的几位数字也会变化(要是在创建过程中停顿几秒)。

MongoDB权威指南读书笔记——概念_数据库


ObjectId的前4个字节是从标准纪元开始的时间戳,单位为秒。时间戳,与随后的5字节组合,提供了秒级别的唯一性。由于时间戳在前,这意味着ObjectId大致会按照插入顺序排列。这4字节也隐含了文档创建的时间。绝大多数驱动程序都会提供一个方法,用于从ObjectId获取这些信息。因为使用的是当前时间,很多用户担心要对服务器进行时间同步。虽然在某些情况下,在服务器间进行时间同步确实是好主意,但是这里其实没有必要,因为时间戳的实际值并不重要,只要它总是不停增加就好。接下来的3字节是所在主机的唯一标识符。通常是机器主机名的散列值。为了确保在同一台机器上并发的多个进程产生的ObjectId是唯一的,接下来的两字节来自产生ObjectId的进程的进程标识符(PID)。

最后3字节是一个自动增加的计数器,确保相同进程同一秒产生的ObjectId也是不一样的。

如果插入文档时没有“_id”键,系统会自动创建一个。可以由MongoDB服务器来做这件事,但通常会在客户端由驱动程序完成。

集合

集合(collection)就是一组文档,可以看作是一个拥有动态模式(dynamic schema)的表,如果将MongoDB中的一个文档比喻为关系型数据库中的一行,那么一个集合就相当于一张表。动态模式指的是一个集合里面的文档可以是各式各样的。例如,下面两个文档可以存储在同一个集合里面:

{"greeting" : "Hello, world!"}
{"foo" : 5}

如果把各种各样的文档不加区分地放在同一个集合里,无论对开发者还是管理员来说都将是噩梦。开发者要么确保每次查询只返回特定类型的文档,要么让执行查询的应用程序来处理所有不同类型的文档。在一个集合里查询特定类型的文档在速度上也很不划算,分开查询多个集合要快得多。例如,假设集合里面一个名为"type"的字段用于指明文档是skim、whole还是chunky monkey。那么,如果从一个集合中查询这三种类型的文档,速度会很慢。但是如果将这三种不同类型的文档拆分为三个不同的集合,每次只需要查询相应的集合,速度快得多。把同种类型的文档放在一个集合里,数据会更加集中。从一个只包含博客文章的集合里查询几篇文章,或者从同时包含文章数据和作者数据的集合里查出几篇文章,相比之下,前者需要的磁盘寻道操作更少。创建索引时,需要使用文档的附加结构(特别是创建唯一索引时)。索引是按照集合来定义的。在一个集合中只放入一种类型的文档,可以更有效地对集合进行索引。
集合使用名称进行标识。集合名可以是满足下列条件的任意UTF-8字符串。集合名不能是空字符串。集合名不能包含\0字符,这个字符表示集合名的结束。集合名不能以“system.”开头,这是为系统集合保留的前缀。用户创建的集合不能在集合名中包含保留字符’$’。因为某些系统生成的集合中包含该字符。
子集合:组织集合的一种惯例是使用“.”分隔不同命名空间的子集合。例如,一个具有博客功能的应用可能包含两个集合,分别是blog.posts和blog.authors。这是为了使组织结构更清晰,这里的blog集合(这个集合甚至不需要存在)跟它的子集合没有任何关系。虽然子集合没有任何特别的属性,但是很多攻击都使用了子集合。GridFS(一种用于存储大文件的协议)使用子集合来存储文件的元数据,这样就可以与文件内容块很好地隔离开来。

Capped collections是固定大小的集合。它有队列过期的特性(过期按照插入的顺序),非常适合类似记录日志。Capped collections按照文档插入顺序保存到集合中,而这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当更新capped collections中文档时,更新后的文档不可超过之前文档的大小,这样就可以确保所有文档在磁盘上的位置一直保持不变。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。
显示的创建一个capped collection,指定一个collection的大小,单位是字节。

db.createCollection("mycoll", {capped:true, size:100000});

在capped collection中,能添加新的对象,能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败。使用Capped Collection不能删除一个文档,可以使用drop()方法删除collection所有的行。删除之后,必须显示的重新创建这个collection。

RDBMS

MongoDB

数据库

数据库

表格

集合


文档


字段

主键

主键 (MongoDB 提供了 key 为 _id )

表联合

嵌入文档

系列文章:
​第二篇 MongoDB权威指南读书笔记——CRUD

参考:https://www.runoob.com/mongodb/mongodb-tutorial.html