8.1 MongoDB 简介

MongoDB 是用 C++ 语言编写的非关系型数据库。特点是高性能、易部署、易使用,存储数据十分方便,主要特性有:

  • 面向集合存储,易于存储对象类型的数据;
  • 模式自由;
  • 支持动态查询;
  • 支持完全索引,包含内部对象;
  • 支持复制和故障恢复;
  • 使用高效的二进制数据存储,包括大型对象;
  • 文件存储格式为 BSON(一种JSON的扩展);

MongoDB 有广泛的使用场景,是传统关系型数据的有效补充。

  • 网站数据:MongoDB 非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
  • 缓存:由于性能很高,MongoDB 也适合作为信息基础设施的缓存层。在系统重启之后,由MongoDB 搭建的持久化缓存可以避免下层的数据源过载。
  • 大尺寸、低价值的数据:使用传统的关系数据库存储一些数据时可能会比较贵,在此之前,很多程序员往往会选择传统的文件进行存储。
  • 高伸缩性的场景:MongoDB 非常适合由数十或者数百台服务器组成的数据库。
  • 用于对象及 JSON 数据的存储:MongoDB 的 BSON 数据格式非常适合文档格式化的存储及查询。

2018 年 6 月,MongoDB 在 4.0 版本中支持 ACID 事务,成为第一个支持强事务的 NoSQL 数据库,更加扩大了其使用的范围。

8.1.1 基本概念

在关系型数据库中,主要由数据库(database)表(table)记录(rows) 三个层次概念组成。

MongoDB 数据库中,主要由数据库(database)集合(collection)文档对象(document) 三个层次概念组成。

对比项 MongoDB MySQL/Oracle
集合 二维表table
表的一行数据 文档document 一条记录record
表字段 键key 字段field
字段值 值value 值value
主外键 PK,FK
灵活度扩展性 极高

MongoDB 里的集合对应于关系型数据库里的表,但是集合中没有列、行和关系的概念,集合中只有文档,一个文档就相当于一条记录,这体现了模式自由的特点。

  • 文档(document)是 MongoDB 中数据的基本单元,非常类似于关系型数据库系统中的行(但是比行要复杂的多);
  • 集合(collection)就是一组文档,如果说 MongoDB 中的文档类似于关系型数据库中的行,那么集合就如同表;

如下为官方给出的文档(document)示例,是典型的JSON数据。

{
  "_id": "5cf0029caff5056591b0ce7d",
  "firstname": "Jane",
  "lastname": "Wu",
  "address": {
    "street": "1 Circle Rd",
    "city": "Los Angeles",
    "state": "CA",
    "zip": "90404"
  },
  "hobbies": ["surfing", "coding"]
}

其使用的查询语言也非常方便,简单易懂。

> db.users.find({ "address.zip" : "90404" })
{ "_id": "5cf0029caff5056591b0ce7d", "firstname": "Jane", "lastname": "Wu", "address":{}}
{ "_id": "507f1f77bcf86cd799439011", "firstname": "Jon", "lastname": "Davis", "address":{}}
{ "_id": "5349b4ddd2781d08c09890f3", "firstname": "Jim", "lastname": "White", "address":{}}
{ "_id": "5bf142459b72e12b2b1b2cd", "firstname": "Jeff", "lastname": "Taylor", "address":{}}
{ "_id": "5cf003283b23d04a40d5f88a", "firstname": "Jerry", "lastname": "Miller", "address":{}}
{ "_id": "5bf142459b72e12b2b1b2cd", "firstname": "Jai", "lastname": "Williams", "address":{}}
{ "_id": "5cf0036deaa1742dd225ea35", "firstname": "Jess", "lastname": "Johnson", "address":{}}
{ "_id": "54495ad94c934721ede76d90", "firstname": "Jill", "lastname": "Brown", "address":{}}
{ "_id": "566eb3c704c7b31facbb0007", "firstname": "Janet", "lastname": "Jones", "address":{}}
{ "_id": "5a999cc461d36489a27f2563", "firstname": "Jan", "lastname":

或者这样支持事务的复杂查询:

session.start_transaction()
order = { line_items : [ { item : 5, quantity: 6 } ] }
db.orders.insertOne( order, session=session );
for x in order.line_items:
  db.inventory.update(
    { _id  : x.item } ,
    { $inc : { number : -1 * x.quantity } },
    session=session
  )
session.commit_transaction()

MongoDB 的单个实例可以容纳多个独立的数据库,每一个数据库都有自己的集合和权限。

MongoDB 自带简洁但功能强大的JavaScript Shell,这个工具对于管理 MongoDB 实例和操作数据作用非常大。

每一个文档都有一个特殊的键 “_id” ,它在文档所处的集合中是唯一的,相当于关系数据库中的表的主键。

MongoDB 支持的数据类型,如下表所示:

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID 对象 ID。用于创建文档的 ID。
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类型。用于存储正则表达式。

8.1.2 下载安装

MongoDB 官方提供有 Windows、Linux、macOS 等操作系统的安装包下载。本小节使用 Windows 版本进行介绍。

到官方网站 https://www.mongodb.com/download-center/community 下载 Windows x64 最新 4.2.1 版本的 MSI 安装包。

双击下载的mongodb-win32-x86_64-2012plus-4.2.1-signed.msi安装文件,启动安装过程。

选择“完全”安装。

在“Install MongoDB Compass”安装步骤中,取消“Install MongoDB Compass”复选框,否则安装程序会到网上下载这个 GUI 管理工具,由于网络下载原因,会导致安装过程特别漫长。

剩余安装步骤,全部选择默认即可。

安装完毕后,检查 Windows 服务,确认 MongoDB 服务已经启动。

如果读者想使用官方 GUI 管理工具(MongoDB Compass),可以自行到 https://www.mongodb.com/download-center/compass 这里使用下载工具(如迅雷)快速下载,并自己安装。

8.1.3 安装管理工具

连接 MongoDB 有多种方式,除了可以使用 MongoDB 自带的最原始的命令窗口工具外,还可以使用功能强大的“NoSQL Manager for MongoDB”客户端 GUI 管理工具来连接 MongoDB。

到 mongodbmanager 官网 https://www.mongodbmanager.com/download 下载Free版本的管理工具,按照正常步骤安装即可。

安装完毕后,打开管理工具,连接到本地 MongoDB 服务器。

通过 GUI 管理工具,可以方便地看到 MongoDB 安装完毕后,默认创建了 admin、config 和 local 等 3 个数据库。

在 Database 上右键,创建数据库 test,并在 test 数据库中创建 test_collection。

在 test_collection 中创建一个文档(document),内容为官方网站首页给出的示例数据。

保存后,在界面中可查看到数据已正确保存。

如果读者安装了 MongoDB Compass,可以打开 MongoDB Compass 查看保存在 MongoDB 服务器中的数据。

个人感觉,其界面不如 NoSQL Manager for MongoDB 看起来方便。当然,读者可以选用自己顺手的 GUI 管理工具,或者直接使用命令行工具来管理,最终效果都是一样的。

8.1.4 常用命令

在资源管理器(C:\Program Files\MongoDB\Server\4.2\bin)下找到mongo.exe,双击,打开 MongoDB 的命令行管理终端。

接下来,我们介绍一些 MongoDB 的命令,熟悉一下 MongoDB 的操作,更详细的 MongoDB 的操作,请参考官方文档。

显示所有的数据库:

> show dbs;
admin   0.000GB
config  0.000GB
local   0.000GB
test    0.000GB

切换到前面创建的 test 数据库:

> use test;
switched to db test

创建一个名为 user 的集合:

> db.createCollection("user");
{ "ok" : 1 }

显示数据库中所有的集合:

> show collections;
empty_collection
test_collection
user

在 user 集合中插入 2 个文档:

> db.user.insert({"id":1,"name":"Kevin","age":"18"});
WriteResult({ "nInserted" : 1 })
> db.user.insert({"id":2,"name":"Roy","age":"8"});
WriteResult({ "nInserted" : 1 })

查询 user 集合中的所有文档:

> db.user.find();
{ "_id" : ObjectId("5de3d41cb422c3774fecc6e8"), "id" : 1, "name" : "Kevin", "age" : "18" }
{ "_id" : ObjectId("5de3d4cfb422c3774fecc6e9"), "id" : 2, "name" : "Roy", "age" : "8" }

查询 user 集合中 name=Kevin 的文档:

> db.user.find({"name":"Kevin"});
{ "_id" : ObjectId("5de3d41cb422c3774fecc6e8"), "id" : 1, "name" : "Kevin", "age" : "18" }

更新 user 集合中 id=1 的文档的 name 为 Kevin Zhang,并添加属性 smart,值为 The Best,并查询所有记录检查更新结果:

> db.user.update({"id":1}, {"id":1,"name":"Kevin Zhang","age":"18","smart":"The Best"})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.user.find();
{ "_id" : ObjectId("5de3d41cb422c3774fecc6e8"), "id" : 1, "name" : "Kevin Zhang", "age" : "18", "smart" : "The Best" }
{ "_id" : ObjectId("5de3d4cfb422c3774fecc6e9"), "id" : 2, "name" : "Roy", "age" : "8" }

再添加一个文档(查询结果为 3 个文档),供后续删除练习用:

> db.user.insert({"id":3,"name":"Good Man","age":"16"});
WriteResult({ "nInserted" : 1 })
> db.user.find();
{ "_id" : ObjectId("5de3d41cb422c3774fecc6e8"), "id" : 1, "name" : "Kevin Zhang", "age" : "18", "smart" : "The Best" }
{ "_id" : ObjectId("5de3d4cfb422c3774fecc6e9"), "id" : 2, "name" : "Roy", "age" : "8" }
{ "_id" : ObjectId("5de3d790b422c3774fecc6ea"), "id" : 3, "name" : "Good Man", "age" : "16" }

删除 id=3 的文档,并查询检查,确认查询结果为 2 个文档:

> db.user.remove({"id":3});
WriteResult({ "nRemoved" : 1 })
> db.user.find();
{ "_id" : ObjectId("5de3d41cb422c3774fecc6e8"), "id" : 1, "name" : "Kevin Zhang", "age" : "18", "smart" : "The Best" }
{ "_id" : ObjectId("5de3d4cfb422c3774fecc6e9"), "id" : 2, "name" : "Roy", "age" : "8" }

通过 GUI 管理工具,查看数据库中集合文档的变化情况,可以清晰得看到 test 数据库中新增了 1 个 user 集合,这个集合中有 2 个文档,并且这两个文档的字段数不相同(有别于关系型数据库如 MySQL,同一张表中字段必须相同)。

下一节:Spring Boot 对常见的数据源都通过“Spring Data”项目提供了支持,当然,MongoDB 就是通过“Spring Data MongoDB”来支持的。所有的 Spring Data 子项目的使用方式都基本一致:使用模板(提供 xxxTemplate 类,如 MongoTemplate、RedisTemplate)和类似于 JPA 的 Repository 类(如 MongoRepository 接口的 SimpleMongoRepository 实现类)。