MongoDB

MongoDB 是一个基于分布式文件存储的数据库,截止当前日期(2019-08-05) 最新版本是 MongoDB 4.0。MongoDB由 C++ 语言编写,旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。
MongoDB 将数据存储为一个文档,数据结构由键值对(key=>value)组成。 MongoDB 文档类型似于 JSON 对象。字段值可以包含其他文档,数组及文档数组等。
你可以在 MongoDB 官网 下载安装包
MongoDB可视化工具有 Robomongo 等。
MongoDB的文档写的很好可惜没有中文版的,英语不错的同学可以直接访问 MongoDB文档官网

MongoDB 命令

Windows系统下的命令如下,其他系统类似:

  1. 运行Mongo服务器
1
C:\mongodb\bin\mongod --dbpath c:\data\db
  1. 启动命令行界面连接MongoDB(打开MongoDB Shell)
1
C:\mongodb\bin\mongo.exe --port 28015

默认连接27017端口,可以通过 --port 指定端口
可以通过 --host <host>:<port> 指定连接远程实例

  1. 安装MongoDB服务
1
C:\mongodb\bin\mongod.exe --config "C:\mongodb\mongod.cfg" --install
  1. 启用MongoDB服务
1
net start MongoDB
  1. 关闭MongoDB服务
1
net stop MongoDB
  1. 移除MongoDB服务
1
C:\mongodb\bin\mongod.exe --remove

MongoDB 概念解析

SQL概念 MongoDB概念 解释
database database 数据库
table collection 表/集合
row document 行/文档
column field 列or字段/域
index index 索引
primary key primary key 主键,MongoDB自动将 _id 域设置为主键

Database

一个mongodb中可以创建多个数据库。
MongoDB的默认数据库为”db”,该数据库存储在data目录中。
MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。
数据库名可以是满足以下条件的任意UTF-8字符串。

  • 不能是空字符串(””)。
  • 不得含有’ ‘(空格)、.、$、/、\和\0 (空字符)。
  • 应全部小写。
  • 最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

数据库名 作用
admin 从权限的角度来看,这是”root”数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
local 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
config 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

Collection

集合就是 MongoDB 文档组。
集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

合法的集合名需要满足以下条件。

  • 集合名不能是空字符串””。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以”system.”开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

Document

文档是一组键值(key-value)对(即 BSON )。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

需要注意的是:

  • 文档中的键/值对是有序的。
  • 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  • MongoDB区分类型和大小写。
  • MongoDB的文档不能有重复的键。
  • 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

  • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
    .和$有特别的意义,只有在特定环境下才能使用。
  • 以下划线”_”开头的键是保留的(不是严格要求的)。

BSON Types

BSON是一种在MongoDB中使用的用来存储文档和进行远程存储过程调用二进制序序列化格式。

类型 数字 别名 备注
Double 1 “double” 双精度浮点值。用于存储浮点值。
String 2 “string” 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Object 3 “object” 用于内嵌文档。
Array 4 “array” 用于将数组或列表或多个值存储为一个键。
Binary data 5 “binData” 二进制数据。用于存储二进制数据。
Undefined 6 “undefined” 废除
ObjectId 7 “objectId” 对象 ID。用于创建文档的 ID。
Boolean 8 “bool” 布尔值。用于存储布尔值(真/假)。
Date 9 “date” 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息
Null 10 “null” 用于创建空值。
Regular Expression 11 “regex” 正则表达式类型。用于存储正则表达式。
DBPointer 12 “dbPointer” 废除
JavaScript 13 “javascript” 代码类型。用于在文档中存储 JavaScript 代码。
Symbol 14 “symbol” 废除
JavaScript (with scope) 15 “javascriptWithScope” -
32-bit integer 16 “int” 23位整型数值。用于存储数值
Timestamp 17 “timestamp” 时间戳。记录文档修改或添加的具体时间。
64-bit integer 18 “long” 64位整型数值。用于存储数值
Decimal128 19 “decimal” 3.4版本新加
Min key -1 “minKey” -
Max key 127 “maxKey” -

ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes:

  • 前 4 个字节表示文档创建时间 unix 时间戳
  • 中间 5 个字节是随机数
  • 最后 3 个字节是以随机数开始的计数器

MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象

由于 ObjectId 中保存了创建的时间戳,所以你可以通过 ObjectId().getTimestamp() 函数来获取创建时间:

String

BSON 字符串只能是 UTF-8 编码。
内部 sort() 方法使用了 C++ 的 strcmp API,所以部分排序可能并不正确。

Timestamp

Timestamp是用在MongoDB内部的时间戳类型。

  • 前 32 位是一个 time_t 值(与Unix新纪元相差的秒数)
  • 后 32 位是在某秒中操作的一个递增的序数

Date

BSON Date 是一个64位整型,表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。

MongoDB 连接

1
mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[database][?options]]
  • mongodb:// mongodb协议头。

  • username:password@ 可选项,如果设置,在连接数据库服务器之后,驱动都会尝试登录这个数据库。

  • hostX 必须的指定至少一个host, host1 是这个URI唯一要填写的。它指定了要连接服务器的地址。如果要连接复数集,请指定多个主机地址。

  • portX 可选的指定端口,如果不填,默认为27017。

  • /database 如果指定username:password@,连接并验证登录指定数据库。若不指定,默认打开 test 数据库。

  • ?options 是连接选项。如果不使用/database,则前面需要加上/。所有连接选项都是键值对name=value,键值对之间通过&或;(分号)隔开。

MongoDB shell 方法

参见mongo Shell Methods,以下列举一些常用的方法:

数据库(Database)相关

创建数据库

1
use DATABASE_NAME

查看所有数据库

1
show dbs

查看当前数据库名

1
db

删除当前数据库

1
db.dropDatabase()

查看当前数据库状态

1
db.stats()

集合(Collection)相关

创建集合/视图

1
db.createCollection(<name>, <options>)
  • name: 要创建的集合名称
  • options: 可选参数,指定有关内存大小及索引的选项

options 可以是如下参数:

字段 类型 描述
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。当该值为 true 时,必须指定 size 参数。
autoIndexId 布尔 (可选)如为 true,自动在 _id 字段创建索引。默认为 false。
size 数值 (可选)为固定集合指定一个最大值(以字节计)。如果 capped 为 true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。

查看当前Database下所有集合

1
2
show collections
show tables

删除集合

1
db.COLLECTION_NAME.drop()

文档(Document)相关

插入文档

1
2
3
db.COLLECTION_NAME.insert(<document>)
db.COLLECTION_NAME.insertOne(<document>)
db.COLLECTION_NAME.insertMany(<document>)

如果 COLLECTION_NAME 该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档.

  • document: 待插入的文档

更新文档

1
2
3
4
5
6
7
db.COLLECTION_NAME.update(
<filter>,
<update>,
<options>
)
db.COLLECTION_NAME.updateOne(...)
db.COLLECTION_NAME.updateMany(...)
  • filter : update的查询条件。
  • update : update的对象和一些更新的操作符(如$,$inc…)等。
  • options : 可选参数,指定更新文档的选项

options可以是如下参数:

字段 类型 描述
upsert boolean 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入
multi boolean 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新
writeConcern document 可选,写级别
collation document 可选,数据的校验
arrayFilters [<filterdocument1\>,…] 可选,文档过滤器的数组

删除文档

1
db.COLLECTION_NAME.remove(<query>, <options>)
  • query : 删除的查询条件。

options可以是如下参数:

字段 类型 描述
justOne boolean 可选,true的话只删除第一个找到的文档。默认 false,删除所有符合query的文档
writeConcern document 可选,写级别
collation document 可选,数据的校验

查询文档

1
2
db.COLLECTION_NAME.find(<query>, <projection>)
db.COLLECTION_NAME.findOne(...)
  • query : 查询的条件。
  • projection : 可选,表明文档中需要返回的符合query过滤器的域,忽略此参数时返回所有的域

索引(Index)相关

MongoDB 索引使用 B-tree 数据结构。MongoDB 会在集合创建时建立唯一索引 _id 且不能删除。MongoDB可以创建复合索引。

创建索引

1
db.COLLECTION_NAME.createIndex( <keys>, <options> )
  • keys 健值对文档,指定索引的字段和排序方式
  • options 可选参数,指定有关索引的其它选项

options可以是如下参数:

字段 类型 描述
background boolean 可选,指定是否在后台创建索引,默认false
unique boolean 可选,指定是否是唯一索引,默认false
name string 可选,指定索引名。不提供的话,MongoDB会将索引名和排序方式连接来生成索引名
partialFilterExpression document 可选,若提供则仅对符合过滤表达式的文档建立索引
sparse boolean 可选,若提供则仅针对提供的field进行索引,这样的话索引占用空间会更小,但对非指定的field进行排序会有影响,默认false
expireAfterSeconds integer 可选,提供一个秒为单位的数字来控制索引的存在时间
storageEngine document 可选
collation document 可选,表明索引的排序规则

删除索引

1
2
db.COLLECTION_NAME.dropIndex(<index name>)
db.COLLECTION_NAME.dropIndexes() // 删除所有索引

查看索引

1
db.COLLECTION_NAME.getIndexes()

强制使用指定索引

1
2
db.COLLECTION_NAME.find(<query>).hint(<string or document>)
db.COLLECTION_NAME.find( { $query: {}, $hint: <string or document> } )

其它常用

聚合

1
db.COLLECTION_NAME.aggregate(pipeline, options)

用于通过管道计算集合/视图数据的聚合结果。

  • pipeline 可以取 聚合管道步骤 的操作符
  • options 可选参数,指定有关聚合的其它选项

格式化

1
db.COLLECTION_NAME.find(<query>).pretty()

排序

1
db.COLLECTION_NAME.find(<query>).sort({<field>: <positive integer>})

将结果根据field进行排序,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

限制返回条数

1
db.COLLECTION_NAME.find(<query>).limit(<positive integer>)

限制返回n条数据

跳过固定条数数据

1
db.COLLECTION_NAME.find(<query>).skip(<positive integer>)

跳过固定n条数数据

查看执行计划

1
2
3
db.COLLECTION_NAME.find(<query>).explain()
db.COLLECTION_NAME.find( { $query: {}, $explain: 1 } )
db.COLLECTION_NAME.explain().find(<query>)

MongoDB 操作符

MongoDB 操作符分为

  • 查询与预测操作符
  • 更新操作符
  • 聚合管道步骤
  • 聚合管道操作符
  • 查询修饰符

以下列举常用的一些操作符

名称 类型 描述 语法
$eq 比较 equal 值相等 { <field>: { $eq: <value> } }
$gt 比较 greater than 值大于 { <field>: {$gt: <value>} }
$gte 比较 greater than equal 值大于等于 { <field>: {$gte: <value>} }
$in 比较 值存在于 { <field>: { $in: [<value1>, <value2>, ... <valueN> ] } }
$lt 比较 less than 值小于 { <field>: {$lt: <value>} }
$lte 比较 less than equal 值小于等于 { <field>: { $lte: <value>} }
$ne 比较 not equal 值不等于 { <field>: {$ne: <value>} }
$nin 比较 not in 值不存在于 { <field>: { $nin: [ <value1>, <value2> ... <valueN> ]} }
$and 逻辑 与,跟表达式数组,表达式之间是与关系 { $and: [ { <expression1> }, { <expression2> } , ... , { <expressionN> } ] }
$not 逻辑 非,跟操作符表达式 { <field>: { $not: { <operator-expression> } } }
$nor 逻辑 异或,跟表达式数组,表达式之间是异或关系 { $nor: [ { <expression1> }, { <expression2> }, ... { <expressionN> } ] }
$or 逻辑 或,跟表达式数组,表达式之间是或关系 { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
$exists 元素 表示field是否存在 { <field>: { $exists: <boolean> } }
$type 元素 表示field需要符合列出的BSON类型,<BSON type>可以是 BSON Types 的别名或者数字 { <field>: { $type: <BSON type> } }
{ <field>: { $type: [ <BSON type1> , <BSON type2>, ... ] } }
$where 评估 $where 查询操作符只对最顶层的document生效,会执行javascript但不走索引,所以尽量先使用其它操作符对数据进行过滤 {$where: function() {<code> return true;}}
$all 数组 field应当是数组,过滤出包含所有给定数组中值的field { <field>: { $all: [ <value1> , <value2> ... ] } }
$elemMath 数组 field应当是数组,过滤出至少有一个元素符合所有给定表达式的field { <field>: { $elemMatch: { <query1>, <query2>, ... } } }
$size 数组 field应当是数组,过滤出所有元素数量符合size的field { <field>: { $size: <positive integer> } }
$comment 评论 为查询添加注释 { <query>, $comment: <comment> }
$sort 聚合 排序,根据给定的field和排序规则对结果进行排序,<sort order>有两种取值,1代表asc,-1代表desc { $sort: { <field1>: <sort order>, <field2>: <sort order> ... } }
$limit 聚合 限制取前n个数据 { $limit: <positive integer> }
$skip 聚合 跳过n个数据 { $skip: <positive integer> }
$group 聚合 分组,通过特定表达式与输出将文档分成不同的group { $group: { _id: <expression>, <field1>: { <accumulator1> : <expression1> }, ... } }

事务与原子性

MongoDB在 4.0版本之后支持了多文档事务,并且计划在4.2版本支持分布式事务。

在MongoDB中,对单文件的操作是原子性的。4.0之后通过复制集合的方式支持了多文档事务。多文档事务支持跨多操作、集合、数据库以及文档,但是大部分情况下相比于单文档写会导致巨大的性能消耗。官方仍旧推荐嵌入式的文档模型而不是多文档事务。而且多文档事务限制较多,具体限制可以查看 事务文档

只有存储引擎 WiredTiger 支持 多文档事务,内存数据引擎或 MMAPv1 存储引擎不支持。

事务例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// Start a session.
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );

employeesCollection = session.getDatabase("hr").employees;
eventsCollection = session.getDatabase("reporting").events;

// Start a transaction
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );

// Operations inside the transaction
try {
employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
} catch (error) {
// Abort transaction on error
session.abortTransaction();
throw error;
}

// Commit the transaction using write concern set at transaction start
session.commitTransaction();

session.endSession();