biancheng-MongoDB教程
目录http://c.biancheng.net/mongodb2/
1NoSQL是什么
2MongoDB是什么
3Windows安装MongoDB
4Linux安装MongoDB
5MacOS安装MongoDB
6MongoDB常用概念
7MongoDB数据类型
8MongoDB数据模型
9MongoDB连接数据库
10MongoDB创建数据库
11MongoDB删除数据库
12MongoDB创建集合
13MongoDB删除集合
14MongoDB插入文档
15MongoDB查询文档
16MongoDB更新文档
17MongoDB删除文档
18MongoDB投影
19MongoDB限制查询条数
20MongoDB排序
21MongoDB索引
22MongoDB聚合查询
23MongoDB复制
24MongoDB分片
25MongoDB备份和恢复
26MongoDB监控
27Java操作MongoDB
28PHP操作MongoDB
29MongoDB关系
30MongoDB DBRefs
31MongoDB覆盖索引查询
32MongoDB查询分析
33MongoDB原子操作
34MongoDB高级索引
35MongoDB索引使用注意事项
36MongoDB ObjectId
37MongoDB MapReduce
38MongoDB全文检索
39MongoDB正则表达式
40MongoDB管理工具
41MongoDB GridFS
42MongoDB固定集合
43MongoDB自动增长
NoSQL是什么
最常见的数据库可以分为下面的两种类型:
- RDBMS(关系型数据库):常见的关系型数据库有 Oracle、DB2、Microsoft SQL Server、Microsoft Access、MySQL;
- NoSQL(非关系型数据库):常见的非关系型数据库有 MongoDB、Redis、Voldemort、Cassandra、Riak、Couchbase、CouchDB 等。
相对于 RDBMS(关系型数据库),NoSQL 具有以下优点:
- 易扩展:NoSQL 数据库种类繁多,但它们都有一个共同的特点,那就是都去掉了关系型数据库的关系型特性,数据与数据之间没有关系,这样就非常容易扩展,无形之间也在架构的层面上带来了可扩展的能力;
- 大数据量,高性能:NoSQL 数据库都具有非常高的读写性能,尤其是在处理庞大数据时表现优秀;
- 灵活:NoSQL 随时都可以存储任意类型的数据,无须提前为要存储的数据建立字段;
- 高可用:NoSQL 在不太影响性能的情况下,就可以方便地实现高可用的架构,比如 Cassandra、HBase 模型,通过复制模型也能实现高可用。
体系框架
NoSQL 整体框架分为四层,由下至上分别为数据持久层(data persistence)、整体分布层(data distribution model)、数据逻辑模型层(data logical model)、和接口层(interface),层次之间相辅相成,协调工作。
1、数据持久层
数据持久层定义了数据的存储形式,主要包括基于内存、硬盘、内存与硬盘相结合、订制可插拔四种形式。
- 基于内存形式的数据存取速度最快,但可能会造成数据丢失;
- 基于硬盘的数据可能保存很久,但存取速度较基于内存的形式慢;
- 内存和硬盘相结合的形式,结合了前两种形式的优点,既保证了速度,又保证了数据不丢失;
- 订制可插拔则保证了数据存取具有较高的灵活性。
2、数据分布层
数据分布层定义了数据是如何分布的,相对于关系型数据库,NoSQL 可选的机制比较多,主要有三种形式:
- CAP 支持:可用于水平扩展;
- 多数据中心支持:可以保证在横跨多数据中心时也能够平稳运行;
- 动态部署支持:可以在运行着的集群中动态地添加或删除节点。
3、数据逻辑层
数据逻辑层表述了数据的逻辑表现形式,与关系型数据库相比,NoSQL 在逻辑表现形式上相当灵活,主要有四种形式:
- 键值模型:这种模型在表现形式上比较单一,却有很强的扩展性;
- 列式模型:这种模型相比于键值模型能够支持较为复杂的数据,但扩展性相对较差;
- 文档模型:这种模型对于复杂数据的支持和扩展性都有很大优势;
- 图模型:这种模型的使用场景不多,通常是基于图数据结构的数据定制的。
4、接口层
接口层为上层应用提供了方便的数据调用接口,提供的选择远多于关系型数据库。接口层提供了五种选择,分别是 Rest、Thrift、Map/Reduce、Get/Put 和特定语言 API,使得应用程序在与数据库交互时更加方便。
NoSQL 分层架构并不代表每个产品在每一层只有一种选择。相反,这种分层设计提供了很大的灵活性和兼容性,每种数据库在不同层面可以支持多种特性。
适用场景
NoSQL 数据库在以下的这几种情况下比较适用:
- 数据模型比较简单;
- 对灵活性要求很强的系统;
- 对数据库性能要求较高;
- 不需要高度的数据一致性;
- 对于给定 key,比较容易映射复杂值的环境。
MongoDB
市面上的数据库软件众多,我们为什么要选择 MongoDB 呢?换句话说就是 MongDB 有什么优势呢?下面列举了几点原因。
1) 面向文档
由于 MongoDB 是 NoSQL 类型的数据库,它不是像关系类型数据库那样以固定的格式存储数据,而是将数据存储在文档中,这使 MongoDB 非常灵活,可以适应实际的商业环境和需求;
2) 临时查询
MongoDB 支持按字段、范围和正则表达式查询并返回文档中的数据;
3) 索引
可以创建索引以提高 MongoDB 中的搜索性能,文档中的任何字段都可以建立索引;
4) 复制
MongoDB 支持高可用性的副本集。副本集由两个或多个 MongoDB 数据库实例组成,每个副本集成员可以随时充当主副本或辅助副本的角色,主副本是与客户端交互并执行所有读/写操作的主服务器。辅助副本使用内置复制维护主副本种的数据。当主副本失败时,副本集将自动切换到辅助副本,然后将辅助副本作为主服务器;
5) 负载平衡
MongoDB 可以在多台服务器上运行,以平衡负载或复制数据,以便在硬件出现故障时保持系统正常运行。
适用场景
MongoDB 的主要目标是在键/值存储方式和传统的 RDBMS(关系型数据库)系统之间架起一座桥梁,它集两者的优势于一身。根据官方网站的描述,MongoDB 适用于以下场景。
1) 网站数据
MongoDB 非常适合实时的插入、更新与查询数据,并具备网站实时存储数据所需的复制及高度伸缩的特性;
2) 缓存
由于性能很高,MongoDB 也适合作为信息基础设施的缓存层,在系统重启之后,由 MongoDB 搭建的持久化缓存层可以避免下层的数据源过载;
3) 庞大的、低价值的数据
使用传统的关系型数据库存取大量数据时,数据库的运行效率往往并不尽人意,而 MongoDB 的出现使这个问题迎刃而解,MongoDB 非常适合庞大数据的存储;
4) 高伸缩性的场景
MongoDB 内置了 MapReduce 引擎,因此非常适合由数十或数百台服务器组成的数据库;
5) 用于对象及 JSON 数据的存储
MongoDB 的 BSON 数据格式非常适合文档化格式的存储及查询。
MacOS安装MongoDB(图解)
下载 MongoDB
https://www.mongodb.com/try/download/community
与在 Linux 系统下安装 MongoDB 相同,首先我们需要在 MongoDB 的官网获得 MongoDB 安装包的下载链接,如下图所示:
得到下载链接后,使用cd
命令进入 /usr/local 目录,然后使用wget
命令下载 MongoDB 的压缩包,命令如下:
cd /usr/local
sudo wget https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-4.4.3.tgz
安装 MongoDB
待压缩包下载完成后就可以尝试安装 MongoDB 了,具体的安装步骤如下:
【步骤 1】解压缩刚刚下载的压缩包,并将其重命名为 mongodb:
sudo tar -zxvf mongodb-macos-x86_64-4.4.3.tgz # 解压 MongoDB 压缩包
sudo mv mongodb-osx-ssl-x86_64-4.0.17/ mongodb # 重命名解压得到的文件夹
【步骤 2】在 /usr/local/mongodb 目录下新建两个文件夹 data 和 log,用于存储 MongoDB 的数据和日志。
sudo mkdir -p /usr/local/mongodb/data
sudo mkdir -p /usr/local/mongodb/log
使用如下命令为当前用户分配目录的读写权限:
sudo chown biancheng /usr/local/mongodb/data
sudo chown biancheng /usr/local/mongodb/log
其中“biancheng”为当前的用户名,您需要修改为您当前系统的用户名。
【步骤 3】配置 PATH。在终端中输入open -e .bash_profile
命令打开 bash_profile 配置文件,然后将 MongoDB 的安装目录下的 bin 目录添加到环境变量中,如下所示:
export PATH=${PATH}:/usr/local/mongodb/bin
编辑完成后保存并退出,然后使用source .bash_profile
命令使配置立即生效。
【步骤 4】使用下面的命令来启动 MongoDB 服务:
mongod --dbpath /usr/local/mongodb/data --logpath /usr/local/mongodb/log/mongo.log --fork
参数说明如下:
- --dbpath 用来设置数据的存放目录;
- --logpath 用来设置日志的存放目录;
- --fork 用来设置在后台运行。
至此 MongoDB 就安装完成了。
验证安装
您可以使用mongod -version
命令来验证 MongoDB 是否安装成功,如果出现类似下面所示的内容,则说明 MongoDB 安装成功。
mongod -version
db version v4.0.10
git version: c389e7f69f637f7a1ac3cc9fae843b635f20b766
allocator: tcmalloc
modules: none
build environment:
distmod: 2008plus-ssl
distarch: x86_64
target_arch: x86_64
MongoDB常用概念解析
集合
集合就是一组 MongoDB 文档的组合,类似于关系型数据库(例如 MySQL)中的数据表。集合存在于数据库中,且没有固定的结构,您可以向集合中插入不同格式或类型的数据。
文档
文档是 MongoDB 中数据的基本单位,由 BSON 格式(一种计算机数据交换格式,类似于 JSON)的键/值对组成,类似于关系型数据库中的一行行数据,但要相对复杂一些。
文档具有动态模式,所谓动态模式就是同一集合中的文档不需要具有相同的字段,即使是相同的字段也可以是不同的类型,这与关系型数据库有很大的区别,也是 MongoDB 最突出的特点之一。
下表列举了关系型数据库与 MongoDB 中的一些差异:
关系型数据库 | MongoDB | 解释说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据表/集合 |
row | document | 数据行/文档 |
column | field | 字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB 中不支持 | |
primary key | primary key | 主键,MongoDB 会自动将 _id 字段设置为主键 |
下面的示例中展示了一个简单的文档结构:
MongoDB数据类型汇总
MongoDB 中常用的几种数据类型:
数据类型 | 描述 |
---|---|
String | 字符串类型,是最常用的数据类型,不过在 MongoDB 中,只有 UTF-8 编码的字符串才是合法的 |
Integer | 整型,用于存储数值。根据您使用服务器的不同,整型可以分为 32 位或 64 位两种 |
Boolean | 布尔型,用于存储布尔类型的值(true/false) |
Double | 双精度浮点型,用于存储浮点型(小数)数据 |
Min/Max keys | 将一个值与 BSON 元素的最低值和最高值相对比 |
Array | 数组类型,用于将数组、列表或多个值存储在一个键中 |
Timestamp | 时间戳,记录文档修改或添加的具体时间 |
Object | 用于内嵌文档 |
Null | 用于创建空值 |
Symbol | 符号,该数据类型于字符串类型类似,不同的是,它一般用于采用特殊符号类型的语言 |
Date | 日期时间,用 UNIX 时间格式来存储当前日期或时间,您可以创建 Date 对象并将 date、month、year 的值传递给 Date 对象来指定自己的日期时间 |
Object ID | 对象 ID,用于创建文档的 ID |
Binary Data | 二进制数据,用于存储二进制数据 |
Code | 代码类型,用于在文档中存储 JavaScript 代码 |
Regular expression | 正则表达式类型,用于存储正则表达式 |
下面简单介绍一下其中几个比较重要的类型:
Object ID
Object ID 类似于关系型数据库中的主键 ID,在 MongoDB 中 Object ID 由 12 字节的字符组成,其中:
- 前 4 个字节表示当前的 Unix 时间戳;
- 之后的 3 个字节是当前设备的机器标识码;
- 再之后的 2 个字节是 MongoDB 服务器的进程 id;
- 最后 3 个字节为计数器,计数器的起始值随机获得。
601e2b6b aa203c c89f 2d31aa ↑ ↑ ↑ ↑ 时间戳 机器码 进程id 计数器
Timestamps
Timestamps(时间戳)类型用来在 MongoDB 的内部使用,与 Date 类型不同,Timestamps 由一个 64 位的值构成,其中:
- 前 32 位是一个 Unix 时间戳(由 Unix 纪元(1970.1.1)开始到现在经过的秒数);
- 后 32 位是一秒内的操作序数。
在一个 MongoDB 实例中,时间戳是唯一的,而且主要在 MongoDB 内部使用,在应用开发中,您可以使用 Date 类型来定义时间日期。
Date
Date 类型是一个 64 位的对象,其中存放了从 Unix 纪元(1970.1.1)开始到现在经历的毫秒数,Date 类型是有符号的,负值则表示 1970.1.1 之前的时间。
> var mydate = new Date()
> mydate
ISODate("2021-02-06T08:50:37.024Z")
> typeof mydate
object
数据模型设计
嵌入式数据模型
嵌入式数据模型也称为非规范化数据模型,在该模型中您可以将所有相关的数据存储到一个文档中,例如我们在三个不同的文档中分别存储了一个员工的个人信息、联系方式和地址等信息,您还可以将这些信息整合到一个文档中,如下所示:
_id: ObjectId("601f4be6e646844cd045c8a4"),
Emp_ID: "10025AE336",
Personal_details:{
First_Name: "Radhika",
Last_Name: "Sharma",
Date_Of_Birth: "1995-09-26"
},
Contact: {
e-mail: "biancheng.net@gmail.com",
phone: "9848022338"
},
Address: {
city: "Hyderabad",
Area: "Madapur",
State: "Telangana"
}
规范化数据模型
在规范化数据模型中,您可以通过引用来将原始文档与子文档关联起来,例如您可以将上面的文档信息以规范化数据模型重写为以下几个文档:
Employee:
{
_id: ObjectId("601f4be6e646844cd045c8a4"),
Emp_ID: "10025AE336"
}
Personal_details:
{
_id: ObjectId("601f50bae646844cd045c8a5"),
empDocID: ObjectId("601f4be6e646844cd045c8a4"),
First_Name: "Radhika",
Last_Name: "Sharma",
Date_Of_Birth: "1995-09-26"
}
MongoDB连接数据库
您只需要在 MongoDB 安装目录下的 bin 目录中执行mongo
命令即可
MongoDB创建数据库
如果我们想要创建一个名为 biancheng 的数据库,则可以使用如下所示的命令:
> use biancheng switched to db biancheng
数据库创建成功后,您可以使用db
命令来查看当前选择的数据库,如下所示:
> db biancheng
如果您想查看 MongoDB 中所有的数据库,则可以使用show dbs
命令,如下所示:
> show dbs admin 0.000GB config 0.000GB local 0.000GB
通过上面的运行结果您会发现,我们刚刚创建的数据库(biancheng)并不在列表中,这是因为该数据库中并没有数据,要显示它您还需要向该数据库中插入一些数据才行,如下所示:
> db.website.insert({title:"编程帮",url:"www.biancheng.net"}) WriteResult({ "nInserted" : 1 }) > show dbs admin 0.000GB biancheng 0.000GB config 0.000GB local 0.000GB
MongoDB删除数据库
MongoDB 中删除当前数据库的语法格式如下:
db.dropDatabase()
注意:使用 db.dropDatabase() 命令会删除当前数据库,在删除数据库之前您可以使用 db 命令来查看当前数据库的名称。
MongoDB创建集合
在 MongoDB 中,您可以使用 createCollection() 方法来创建集合,语法格式如下:
db.createCollection(name, options)
参数说明如下:
- name: 要创建的集合名称;
- options: 可选参数, 指定有关内存大小及索引的选项,可选值如下表所示:
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为 true,则创建固定集合,固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档,注意:如果该值为 true,则必须指定 size 参数 |
autoIndexId | 布尔 | (可选)如为 true,则自动在 _id 字段创建索引,默认为 false,注意:MongoDB 3.2 之后不再支持该参数 |
size | 数值 | (可选)为固定集合指定一个最大值,即字节数,如果 capped 为 true,则需要指定该字段 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量 |
当我们在集合中插入文档时,MongoDB 会首先检查固定集合的 size 字段,然后检查 max 字段。
【示例】创建一个名为“bianchengbang”的数据库,并创建一个名为“user”的集合:
> use bianchengbang switched to db bianchengbang > db.createCollection("user") { "ok" : 1 }
集合创建完成后,您可以使用show collections
命令或者show tables
命令来查看数据库中的集合:
> show collections user > show tables user
MongoDB删除集合
db.collection_name.drop()
MongoDB插入文档(3中方法)
insert() 与 save() 方法
您可以使用 MongoDB 中的 insert() 或 save() 方法向集合中插入文档,语法如下:
db.collection_name.insert(document)
db.collection_name.save(document)
语法说明如下:
- save():如果 _id 主键存在则更新数据,如果不存在就插入数据。不过该方法在新版本的 MongoDB 中已废弃,您可以使用 insertOne() 或 replaceOne() 方法来代替;
- insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,并提示主键重复,不保存当前数据。
insertOne() 方法
insert() 方法可以同时插入多个文档,但如果您只需要将一个文档插入到集合中的话,可以使用 insertOne() 方法,该方法的语法格式如下:
db.collection_name.insertOne(document)
insertMany() 方法
与 insert() 方法相同,您可以使用 insertMany() 方法向集合中插入多个文档,但在使用 insertMany() 方法时您需要向方法中传递一个文档数组,如下例所示:
MongoDB查询文档
find() 方法
想要查询集合中的文档,可以使用 MongoDB 中的 find() 方法,find() 方法可以将查询结果以非结构化的方式展示出来,其语法格式如下:
db.collection_name.find(query, projection)
语法说明如下:
- query:可选参数,使用查询操作符指定查询条件;
- projection:可选参数,使用投影操作符指定返回的键。查询时若要返回文档中所有键值,只需省略该参数即可(默认省略)。
MongoDB更新文档
update() 方法
update() 方法用于更新现有文档中的值,其语法格式如下:
db.collection_name.update(
{
upsert:
multi:
writeConcern:
}
)
参数说明如下:
- query:update 的查询条件,类似 SQL 中 update 语句内 where 后面的内容;
- update:update 的对象和一些更新的操作符(如 $、$inc...)等,也可以理解为 SQL 中 update 语句内 set 后面的内容;
- upsert:可选参数,默认值为 false,用来定义当要更新的记录不存在时,是否当作新记录插入到集合中,当值为 true 时表示插入,值为 false 时不插入;
- multi:可选参数,默认值为 false,用来表示只更新找到的第一条记录,当值为 true 时,则把按条件查出来的多条记录全部更新;
- writeConcern:可选参数,用来定义抛出异常的级别。
MongoDB删除文档
db.collection_name.remove(
{
justOne:
writeConcern:
}
)
参数说明:
- query:可选参数,定义要删除文档的条件;
- justOne:可选参数,如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档;
- writeConcern:可选参数,定义抛出异常的级别。
MongoDB limit()与skip():限制查询条数
limit() 方法
MongoDB 中要限制查询的记录条数可以使用 limit() 方法,该方法接收一个数字作为参数,用来设置要显示的记录数,如下所示:
db.collection_name.find().limit(number)
【示例】假设在集合“course”中有如下数据:
> db.course.find() { "_id" : ObjectId("60331a7eee79704753940391"), "title" : "HTML教程", "author" : "编程帮", "url" : "http://www.biancheng.com/html/index.html" } { "_id" : ObjectId("60331a7eee79704753940392"), "title" : "C#教程", "author" : "编程帮", "url" : "http://www.biancheng.com/csharp/index.html" } { "_id" : ObjectId("60331a7eee79704753940393"), "title" : "MongoDB教程", "author" : "编程帮", "url" : "http://www.biancheng.com/mongodb/index.html" }
若要查询其中的两条,则可以像下面这样:
> db.course.find({},{"title":1,_id:0}).limit(2) { "title" : "HTML教程" } { "title" : "C#教程" }
注意:如果没有指定 limit() 方法中的参数,则会显示集合中的所有数据。
skip() 方法
除了 limit() 方法外,MongoDB 中还提供了另一种方法 skip(),它同样也可以接收一个数字类型的参数,用来设置要跳过的文档数。其语法格式如下:
db.collection_name.find().skip(number)
【示例】查询集合中的所有文档,并跳过两条记录:
> db.course.find({},{"title":1,_id:0}).skip(2) { "title" : "MongoDB教程" }
MongoDB排序:sort()方法
db.collection_name.find().sort({key:1})
其中 key 用来定义要根据那个字段进行排序,后面的值 1 则表示以升序进行排序,若要以降序进行排序则需要将其设置为 -1。
MongoDB索引
createIndex() 方法
MongoDB 中您可以使用 createIndex() 方法来创建索引,其语法格式如下:
db.collection_name.createIndex(keys, options)
参数说明如下:
- keys:由键/值对组成,其中键用来定义要创建索引的字段,值用来定义创建索引的顺序,1 表示按升序创建索引,-1 表示按降序来创建索引;
- options:可选参数,其中包含一组控制索引创建的选项,可选值如下表所示。
参数 | 类型 | 描述 |
---|---|---|
background | Boolean | 可选参数,当值为 true 时,表示在后台构建索引,避免在创建索引的过程阻塞其它数据库操作,默认值为 false |
unique | Boolean | 创建唯一索引,当值为 true 时表示创建唯一索引,以避免重复数据的插入,默认为 false |
name | string | 索引的名称。如果未指定,MongoDB 将通过连接索引的字段名和排序顺序生成一个索引名称 |
dropDups | Boolean | 在建立唯一索引时是否删除重复记录,设置为 true 则表示创建唯一索引,默认值为 false,3.0 版本之后废弃 |
sparse | Boolean | 对文档中不存在的字段数据不启用索引,这个参数需要特别注意,如果设置为 true 的话,则在索引字段中不会查询出不包含对应字段的文档。默认值为 false |
expireAfterSeconds | integer | 指定一个以秒为单位的数值,完成 TTL 设定,设定集合的生存时间 |
v | index version | 索引的版本号,默认的索引版本取决于 mongod 创建索引时运行的版本 |
weights | document | 索引权重值,数值在 1 到 99999 之间,表示该索引相对于其他索引字段的得分权重 |
default_language | string | 对于文本索引,该语言用于确定停用词列表以及词干分析器和令牌生成器的规则,默认为英语 |
language_override | string | 对于文本索引,指定文档中包含要替代默认语言的语言的字段名称,默认值为 language |
【示例】为集合“course”中的 title 字段创建索引:
> db.course.createIndex({"title":1}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
dropIndex() 方法
除了可以创建索引外,您还可以使用 dropIndex() 方法来删除指定的索引,其语法格式如下:
db.collection_name.dropIndex(index)
其中 index 用来指定要删除的索引,可以是索引名称key
的形式,也可以是{key:1}
的形式。
【示例】删除集合“course”中“title”字段的索引:
> db.course.dropIndex({"title":1}) { "nIndexesWas" : 3, "ok" : 1 }
dropIndexes() 方法
dropIndexes() 方法同样用来删除索引,与 dropIndex() 方法不同 dropIndexes() 方法可以同时删除集合中的多个索引,其语法格式如下:
db.collection_name.dropIndexes()
注意:在使用 dropIndexes() 方法不需要提供任何参数。
【示例】同时删除集合“course”中 title 与 author 字段的索引:
> db.course.dropIndexes() { "nIndexesWas" : 2, "msg" : "non-_id indexes dropped for collection", "ok" : 1 }
getIndexes() 方法
getIndexes() 方法可以获取集合中所有索引,语法格式如下:
db.collection_name.getIndexes()
MongoDB聚合查询
aggregate() 方法
您可以使用 MongoDB 中的 aggregate() 方法来执行聚合操作,其语法格式如下:
db.collection_name.aggregate(aggregate_operation)
若要统计每个作者“author”一共编写了多少教程,可以使用下面的 aggregate() 方法:
> db.course.aggregate([{$group : {_id : "$author", sum : {$sum : 1}}}]) { "_id" : "编程帮", "sum" : 3 }
上述示例类似于 SQL 语句中的SELECT author, count(*) FROM course GROUP BY author
。
MongoDB复制(副本集)
设置副本集
接下来我们来看一下如何将独立的 MongoDB 实例转换为副本集。要转换为副本集,需要按照以下几个步骤操作:
- 关闭正在运行的 MongoDB 服务器;
- 通过指定 --replSet 选项启动 MongoDB 服务器,--replSet 的基本语法如下所示:
mongod --port "PORT" --dbpath "YOUR_DB_DATA_PATH" --replSet "REPLICA_SET_INSTANCE_NAME"
【示例】在 27017 端口上启动名为 rs0 的 mongod 实例:
mongod --port 27017 --dbpath "D:\set up\mongodb\data" --replSet rs0
MongoDB分片
在上图中,有三个主要组件
- Shards(碎片):用于存储实际的数据块,在生产环境中,每个分片都是一个单独的副本集,它们提供了高可用性和数据一致性;
- Config Servers(配置服务器):用于存储集群的元数据,此数据包含集群数据集到碎片的映射。查询路由器使用此元数据将操作定向到特定的碎片。在生产环境中,分片群集恰好具有 3 个配置服务器;
- Query Routers(查询路由器):查询路由器基本上都是 mongo 实例,可与客户端应用程序接口并将操作定向到适当的分片。查询路由器处理操作并将其定位到分片,然后将结果返回给客户端。分片群集可以包含多个查询路由器来划分客户端请求负载。客户端将请求发送到一个查询路由器。通常,分片群集具有许多查询路由器。
MongoDB备份和恢复
数据备份
在 MongoDB 中我们可以使用 mongodump 命令来对 MongoDB 进行数据备份,该命令可以导出所有数据到指定目录中,也可以通过参数将导出数据转存的服务器。其语法格式如下:
mongodump -h dbhost -d dbname -o dbdirectory
参数说明如下:
- -h:MongDB 所在服务器的地址,例如:127.0.0.1,同时也可以指定端口号,例如:127.0.0.1:27017;
- -d:需要备份的数据库实例,例如:test;
- -o:备份数据存放的位置,例如:c:\data\dump,该目录需要提前建立,在备份完成后,系统会自动在 dump 目录下建立一个 test 目录,并在这个目录里面存放该数据库实例的备份数据。
【示例】假设在电脑本地(127.0.0.1)的 27017 端口运行 MongoDB 服务,然后打开命令提示符(CMD)窗口并进入 MongoDB 安装目录下的 bin 目录,最后输入mongodump
命令,运行结果如下:
C:\Users\79330>D:
D:\>cd install/MongoDB/bin
D:\install\Mongodb\bin>mongodump
2021-02-25T11:22:39.468+0800 writing admin.system.version to
2021-02-25T11:22:39.516+0800 done dumping admin.system.version (1 document)
2021-02-25T11:22:39.516+0800 writing bianchengbang.course to
2021-02-25T11:22:39.517+0800 done dumping bianchengbang.course (3 documents)
mongodump
命令将连接到运行于127.0.0.1:27017
的 MongoDB 服务器上,并将服务器中的所有数据备份到 MongoDB 安装目录下的 /bin/dump/ 目录下。
下表中列举了可以与mongodump
命令一起使用的参数列表:
语法 | 描述 | 实例 |
---|---|---|
mongodump --host HOST_NAME --port PORT_NUMBER | 该命令将备份所有 MongoDB 数据 | mongodump --host runoob.com --port 27017 |
mongodump --dbpath DB_PATH --out BACKUP_DIRECTORY | 该命令将仅备份指定路径上的指定数据库 | mongodump --dbpath /data/db/ --out /data/backup/ |
mongodump --collection COLLECTION --db DB_NAME | 该命令将备份指定数据库的集合 | mongodump --collection mycol --db test |
恢复数据
要想还原备份的数据,您可以使用 MongoDB 的mongorestore
命令,此命令会从备份目录还原所有数据,语法格式如下:
mongorestore
【示例】下面通过一个简单的示例演示一下mongorestore
命令的使用:
D:\install\Mongodb\bin>mongorestore
MongoDB监控运行状态
mongostat
mongostat
命令能够检查所有正在运行的 mongod 实例的状态,并返回数据库操作的计数器。这些计数器包括插入、查询、更新、删除和游标。当您的内存不足、写入量不足或者出现一些性能问题时,该命令还会显示发生错误的时间,并显示锁定百分比。
要运行该命令,您需要先启动您的 mongod 实例,并在另一个命令提示符(CMD)中转到 MongoDB 安装目录下的 bin 目录,最后输入mongostat
命令并运行,运行结果如下:
D:\install\Mongodb\bin>mongostat
MongoDB $explain与$hint:查询分析
$explain
在 mongo shell 中,您可以通过 $explain 的辅助方法 explain() 来检索查询的相关信息:
> db.users.find({gender:"M"}, {name:1,_id:0}).explain()
关于上面的运行结果,有以下几点需要说明:
- indexOnly:若字段的值为 true,则表示此查询中使用了索引;
- cursor:指定使用的游标类型,BTreeCursor 类型表示使用了索引,还提供了所用索引的名称,BasicCursor 表示在不使用任何索引的情况下进行了完全扫描;
- n:表示返回的匹配文档数;
- nscannedObjects:表示扫描文档的总数;
- nscanned:表示扫描的文档或索引条目的总数。
$hint
$hint 运算符(也叫“强制查询优化器”)能够使用指定的索引来进行查询,以此来测试查询的性能。当您想要测试具有不同索引的查询性能时,此功能特别有用。在 mongo shell 中您可以使用 $hint 的辅助方法 hint() 来使用此功能,例如下面的查询指定了要使用 gender 和 name 字段的索引:
> db.users.find({gender:"M"},{name:1, _id:0}).hint({gender:1, name:1}) { "name" : "bianchengbang" }
MongoDB原子操作
常用的原子操作命令
$set
用来指定一个键并更新键值,若键不存在则创建。
{ $set : { field : value } }
$unset
用来删除一个键。
{ $unset : { field : 1} }
$inc
用来对文档的某个数值类型的键值进行增减操作。
{ $inc : { field : value } }
$push
用来向文档中追加一些信息。
{ $push : { field : value } }
把 value 追加到 field 里面去,field 一定要是数组类型才行,如果 field 不存在,则会新增一个数组类型加进去。
$pushAll
与 $push 类似,它可以一次追加多个值到一个数组类型的字段内。
{ $pushAll : { field : value_array } }
$pull
从数组 field 内删除一个等于 value 的值。
{ $pull : { field : _value } }
$pop
删除数组的第一个或最后一个元素。
{ $pop : { field : 1 } }
$rename
修改字段的名称。
{ $rename : { old_field_name : new_field_name } }
$bit
位操作,integer 类型。
{$bit : { field : {and : 5}}}
MongoDB ObjectId
MongoDB 中的 ObjectId 的组成,ObjectId 是一个 12 字节的 BSON 类型,具有以下结构:
- 前 4 个字节表示时间戳;
- 接下来的 3 个字节表示机器标识符;
- 紧接着的 2 个字节由进程 id(PID)组成;
- 最后 3 个字节是一个随机计数器的值。
MongoDB 使用 ObjectId 作为每个文档 _id 字段的默认值,而且该值可以在创建文档时自动生成。ObjectId 的复杂组合方式使得所有的 id 字段都是唯一的。
MongoDB MapReduce
mapReduce 是个灵活且强大的数据聚合工具,它的好处是可以把一个聚合任务分解为多个小的任务,分配到多个服务器上并行处理。
在 MongoDB 中我们可以使用 mapReduce 命令来执行 mapReduce 操作。
mapReduce 命令
在 mapReduce 命令中要实现两个函数,分别是 map 函数和 reduce 函数,其中 map 函数调用 emit(key, value),遍历集合中的所有记录,并将 key 与 value 传递给 reduce 函数进行处理,如下所示:
> db.collection_name.mapReduce( function() {emit(key, value);}, // map 函数 function(key, values) {return reduceFunction}, // reduce 函数 { out: collection, query: document, sort: document, limit: number } )
参数说明如下:
- map 函数:一个 javascript 函数,它用一个键映射一个值并发出一个键值对;
- reduce 函数:一个 javascript 函数,用于减少或分组具有相同键的所有文档;
- out:指定 map-reduce 查询结果的位置;
- query:指定用于选择文档的可选选择条件;
- sort:指定可选的排序条件;
- limit:指定要返回的最大文档数(可选)。
使用 mapReduce
假如有一个存储用户帖子信息的集合,其中存储了若干用户的用户名、状态等信息,例如下面这样:
{
"post_text": "编程帮(biancheng.net),一个在线学习编程的网站,专注于分享优质编程教程。",
"user_name": "bianchengbang",
"status":"active"
}
接下来在上述的集合中使用 mapReduce 命令来选择集合中所有"status":"active"
的文档,然后根据用户名对它们进行分组,最后统计每个用户的发帖数量。示例代码如下:
> db.posts.mapReduce( ... function() { emit(this.user_name,1); }, ... function(key, values) {return Array.sum(values)}, ... { ... query:{status:"active"}, ... out:"post_total" ... } ... )
上面 mapReduce 命令的输出结果如下所示:
{ "result" : "post_total", "timeMillis" : 48, "counts" : { "input" : 11, "emit" : 11, "reduce" : 2, "output" : 2 }, "ok" : 1 }
关于运行结果,有如下几点需要说明:
- result:储存结果的 collection 的名字,这是个临时集合,mapReduce 的连接关闭后会被自动删除;
- timeMillis:执行花费的时间,单位为毫秒;
- input:满足条件被发送到 map 函数的文档个数;
- emit:在 map 函数中 emit 被调用的次数,也就是所有集合中的数据总量;
- ouput:结果集合中的文档个数(count 对调试非常有帮助);
- ok:查询是否执行成功,成功为 1;
- err:若执行失败,则会在这里显示失败原因。
通过运行结果可以发现,共有 11 个文档与查询匹配(状态为“active”),map 函数生成了 11 个具有键值对的文档,最后 reduce 函数将具有相同键值的映射文档分为 2 个组。
若要查看 mapReduce 查询的结果,可以使用 find 方法,如下所示:
> db.posts.mapReduce( ... function() { emit(this.user_name,1); }, ... function(key, values) {return Array.sum(values)}, ... { ... query:{status:"active"}, ... out:"post_total" ... } ... ).find()
上面的查询语句,执行结果如下:
{ "_id" : "biancheng", "value" : 5 }
{ "_id" : "bianchengbang", "value" : 6 }
MongoDB全文检索
MongoDB 支持大约 15 种语言的全文索引,例如 danish、dutch、english、finnish、french、german、hungarian、italian、norwegian、portuguese、romanian、russian、spanish、swedish、turkish 等。
启用全文检索
最初,全文检索是一个实验性功能,但 MongoDB 在 2.6 版本以后默认开启了此功能,如果您使用 2.6 之前的版本,则需要使用以下代码来启用全文检索:
>db.adminCommand({setParameter:true, textSearchEnabled:true})
或者使用命令:
mongod --setParameter textSearchEnabled=true
创建全文索引
假如我们在 posts 集合中插入以下文档:
> db.posts.insert([ ... { ... "post_text": "enjoy the mongodb articles on bianchengbang", ... "tags": ["mongodb", "bianchengbang"] ... }, ... { ... "post_text" : "writing tutorials on mongodb", ... "tags" : [ "mongodb", "tutorial" ] ... } ... ]) BulkWriteResult({ "writeErrors" : [ ], "writeConcernErrors" : [ ], "nInserted" : 2, "nUpserted" : 0, "nMatched" : 0, "nModified" : 0, "nRemoved" : 0, "upserted" : [ ] })
若要在 post_text 字段上创建全文索引,以便我们可以直接搜索字段中的内容,可以像下面这样:
> db.posts.createIndex({post_text:"text"}) { "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "numIndexesAfter" : 2, "ok" : 1 }
使用全文索引
使用全文索引可以提高搜索效率,前面我们已经在 post_text 字段上创建了全文索引,下面通过一个示例来演示全文索引的使用。
【示例】搜索 post_text 字段中包含关键词“bianchengbang”的所有文档:
> db.posts.find({$text:{$search:"bianchengbang"}}).pretty() { "_id" : ObjectId("6041dfc3835e4aa734b591df"), "post_text" : "enjoy the mongodb articles on bianchengbang", "tags" : [ "mongodb", "bianchengbang" ] }
MongoDB GridFS
GridFS 是 MongoDB 的一种规范,用于存储和检索大型文件,如图像、音频、视频等。GridFS 也是一种存储文件的文件系统,但其数据是存储在 MongoDB 集合中的,GridFS 甚至可以存储超过 16MB 的文件。在存储文件时 GridFS 可以将一个文件分为多个数据块,并将每个数据块存储在一个单独的文档中,每个文档最大为 255KB。
默认情况下,GridFS 使用 fs.files 和 fs.chunks 两个集合来存储文件的元数据和块。每个区块都由其唯一的 ObjectId(_id)字段标识。fs.files 用作父文档,fs.chunks 文档中的 files_id 字段将块链接到其父级。
下面展示了一个简单的 fs.files 集合文档:
{ "filename": "test.txt", "chunkSize": NumberInt(261120), "uploadDate": ISODate("2014-04-13T11:32:33.557Z"), "md5": "7b762939321e146569b07f72c62cca4f", "length": NumberInt(646) }
该文档指定了文件的文件名、块大小、上传日期和长度。下面展示了一个简单的 fs.chunks 集合文档:
{ "files_id": ObjectId("534a75d19f54bfec8a2fe44b"), "n": NumberInt(0), "data": "Mongo Binary Data" }