MongoDB 概述

什么是 MongoDB

MongoDB 是一个基于分布式文件存储的开源数据库系统由 C++ 编写而成它将数据存储为一个文档数据结构由键值对组成类似于 JSON 对象

  • 核心功能
    • 文档存储以 BSON 格式存储数据
    • 灵活模式无需预定义表结构
    • 水平扩展支持分片集群
    • 丰富查询支持复杂的查询和聚合
  • 主要优势
    • 高性能内存映射引擎读写速度快
    • 高可用副本集自动故障转移
    • 易扩展原生支持分片
    • 灵活性强动态 Schema易于迭代

MongoDB 的核心概念

基本概念

  • Database数据库
    • 相当于关系数据库中的数据库
    • 一个 MongoDB 服务器可以有多个数据库
  • Collection集合
    • 相当于关系数据库中的表
    • 一组文档的集合
  • Document文档
    • 相当于关系数据库中的行
    • 最小的数据单元以 BSON 格式存储
  • Field字段
    • 相当于关系数据库中的列
    • 文档中的键值对
  • Index索引
    • 提高查询性能
    • 支持单字段复合地理空间等索引
  • _id
    • 每个文档的唯一标识
    • 默认生成 ObjectId

架构组件

  • mongod
    • MongoDB 服务器进程
    • 处理数据请求和管理数据
  • mongos
    • 路由服务
    • 用于分片集群
  • mongo shell
    • 交互式 JavaScript 接口
    • 用于管理和管理数据

MongoDB 的工作原理

写入流程

1
2
3
4
5
1. 客户端发送写请求到 mongod
2. mongod 将操作写入 Journal预写日志
3. 更新内存中的数据
4. 定期将内存数据刷新到磁盘
5. 返回结果给客户端

读取流程

1
2
3
4
5
1. 客户端发送读请求
2. mongod 检查内存中是否有数据
3. 如果有直接返回
4. 如果没有从磁盘读取到内存
5. 返回结果给客户端

环境搭建

安装 MongoDB

Docker 安装推荐

1
2
3
4
5
6
7
8
9
10
11
12
# 拉取镜像
docker pull mongo:6.0

# 运行容器
docker run -d \
--name mongodb \
-p 27017:27017 \
-v /path/to/data:/data/db \
mongo:6.0

# 进入容器
docker exec -it mongodb mongosh

Linux 安装

1
2
3
4
5
6
7
8
9
10
11
# Ubuntu/Debian
sudo apt-get install -y mongodb-org

# CentOS/RHEL
sudo yum install -y mongodb-org

# 启动 MongoDB
sudo systemctl start mongod

# 设置开机自启
sudo systemctl enable mongod

Windows 安装

下载地址https://www.mongodb.com/try/download/community

步骤

  1. 下载 MSI 安装包
  2. 运行安装程序
  3. 选择安装路径和数据目录
  4. 启动 MongoDB 服务

macOS 安装

1
2
3
4
5
6
7
8
9
# 使用 Homebrew
brew tap mongodb/brew
brew install mongodb-community@6.0

# 启动
brew services start mongodb-community@6.0

# 停止
brew services stop mongodb-community@6.0

配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# mongod.conf

# 存储配置
storage:
dbPath: /var/lib/mongodb
journal:
enabled: true

# 系统日志
systemLog:
destination: file
logAppend: true
path: /var/log/mongodb/mongod.log

# 网络配置
net:
port: 27017
bindIp: 127.0.0.1

# 安全配置
security:
authorization: enabled

验证安装

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 连接 MongoDB
mongosh

# 或使用旧版客户端
mongo

# 查看版本
db.version()

# 查看所有数据库
show dbs

# 切换数据库
use test

# 插入文档
db.users.insertOne({name: "张三", age: 25})

# 查询文档
db.users.find()

基本操作

数据库操作

创建/切换数据库

1
2
3
4
5
6
7
8
9
10
11
// 切换或创建数据库
use mydb

// 查看当前数据库
db

// 查看所有数据库
show dbs

// 删除当前数据库
db.dropDatabase()

注意事项

1
2
3
4
细节注意
1. use 命令不会立即创建数据库
2. 只有在插入数据后才会真正创建
3. 数据库名区分大小写

集合操作

创建集合

1
2
3
4
5
6
7
8
9
10
11
12
// 显式创建集合
db.createCollection("users")

// 带选项创建
db.createCollection("logs", {
capped: true,
size: 10485760, // 10MB
max: 1000
})

// 隐式创建插入数据时自动创建
db.users.insertOne({name: "李四"})

查看集合

1
2
3
4
5
6
7
8
// 查看所有集合
show collections

// 查看集合统计信息
db.users.stats()

// 查看集合大小
db.users.dataSize()

删除集合

1
2
// 删除集合
db.users.drop()

文档操作

插入文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 插入单个文档
db.users.insertOne({
name: "张三",
age: 25,
email: "zhangsan@example.com",
hobbies: ["读书", "游泳"],
address: {
city: "北京",
street: "长安街"
}
})

// 插入多个文档
db.users.insertMany([
{name: "李四", age: 30},
{name: "王五", age: 28},
{name: "赵六", age: 35}
])

// 指定 _id
db.users.insertOne({
_id: 1,
name: "自定义ID"
})

查询文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 查询所有文档
db.users.find()

// 格式化输出
db.users.find().pretty()

// 条件查询
db.users.find({age: 25})

// 多条件查询AND
db.users.find({age: 25, name: "张三"})

// 范围查询
db.users.find({age: {$gte: 20, $lte: 30}})

// 限制返回数量
db.users.find().limit(10)

// 跳过记录
db.users.find().skip(10)

// 排序
db.users.find().sort({age: 1}) // 升序
db.users.find().sort({age: -1}) // 降序

// 投影选择字段
db.users.find({}, {name: 1, age: 1, _id: 0})

更新文档

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 更新单个文档
db.users.updateOne(
{name: "张三"},
{$set: {age: 26}}
)

// 更新多个文档
db.users.updateMany(
{age: {$lt: 30}},
{$set: {status: "young"}}
)

// 替换文档
db.users.replaceOne(
{name: "张三"},
{name: "张三丰", age: 100}
)

// 原子操作
$set // 设置字段值
$unset // 删除字段
$inc // 递增
$push // 添加到数组
$pull // 从数组删除
$addToSet // 添加到数组不重复

删除文档

1
2
3
4
5
6
7
8
// 删除单个文档
db.users.deleteOne({name: "张三"})

// 删除多个文档
db.users.deleteMany({age: {$lt: 20}})

// 删除所有文档
db.users.deleteMany({})

查询操作符

比较操作符

操作符 说明 示例
$eq 等于 {age: {$eq: 25}}
$ne 不等于 {age: {$ne: 25}}
$gt 大于 {age: {$gt: 25}}
$gte 大于等于 {age: {$gte: 25}}
$lt 小于 {age: {$lt: 25}}
$lte 小于等于 {age: {$lte: 25}}
$in 在数组中 {age: {$in: [20, 25, 30]}}
$nin 不在数组中 {age: {$nin: [20, 25, 30]}}

逻辑操作符

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// AND默认就是 AND
db.users.find({age: 25, name: "张三"})

// OR
db.users.find({
$or: [
{age: 25},
{name: "张三"}
]
})

// NOR
db.users.find({
$nor: [
{age: 25},
{name: "张三"}
]
})

// NOT
db.users.find({
age: {$not: {$gt: 25}}
})

元素操作符

1
2
3
4
5
6
7
8
9
// 字段存在
db.users.find({email: {$exists: true}})

// 字段类型
db.users.find({age: {$type: "number"}})

// 正则表达式
db.users.find({name: {$regex: /^张/}})
db.users.find({name: /张$/})

数组操作符

1
2
3
4
5
6
7
8
9
10
11
// 数组包含某个值
db.users.find({hobbies: "读书"})

// 数组大小
db.users.find({hobbies: {$size: 3}})

// 数组所有元素匹配
db.users.find({scores: {$all: [80, 90]}})

// 数组任意元素匹配
db.users.find({scores: {$elemMatch: {$gte: 90}}})

索引

创建索引

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 单字段索引
db.users.createIndex({name: 1})

// 复合索引
db.users.createIndex({name: 1, age: -1})

// 唯一索引
db.users.createIndex({email: 1}, {unique: true})

// 稀疏索引
db.users.createIndex({email: 1}, {sparse: true})

// TTL 索引自动过期
db.logs.createIndex({createdAt: 1}, {expireAfterSeconds: 3600})

// 文本索引
db.articles.createIndex({content: "text"})

查看索引

1
2
3
4
5
// 查看所有索引
db.users.getIndexes()

// 查看索引大小
db.users.totalIndexSize()

删除索引

1
2
3
4
5
// 删除指定索引
db.users.dropIndex({name: 1})

// 删除所有索引保留 _id
db.users.dropIndexes()

索引类型

类型 说明 适用场景
单字段索引 单个字段的索引 单字段查询
复合索引 多个字段的索引 多字段查询
多键索引 数组字段的索引 数组查询
地理空间索引 地理位置索引 位置查询
文本索引 全文搜索索引 文本搜索
哈希索引 哈希值索引 分片键

聚合管道

基本聚合

1
2
3
4
5
6
7
// 简单聚合
db.orders.aggregate([
{$group: {
_id: "$customerId",
totalAmount: {$sum: "$amount"}
}}
])

常用阶段

阶段 说明 示例
$match 过滤文档 {$match: {status: "A"}}
$group 分组聚合 {$group: {_id: "$dept", count: {$sum: 1}}}
$project 投影字段 {$project: {name: 1, age: 1}}
$sort 排序 {$sort: {age: -1}}
$limit 限制数量 {$limit: 10}
$skip 跳过记录 {$skip: 10}
$unwind 展开数组 {$unwind: "$tags"}
$lookup 关联查询 {$lookup: {...}}

复杂聚合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 销售统计
db.orders.aggregate([
// 过滤
{$match: {
orderDate: {
$gte: new Date("2024-01-01"),
$lt: new Date("2024-12-31")
}
}},

// 展开订单项
{$unwind: "$items"},

// 分组统计
{$group: {
_id: {
year: {$year: "$orderDate"},
month: {$month: "$orderDate"},
product: "$items.productName"
},
totalQuantity: {$sum: "$items.quantity"},
totalAmount: {$sum: {$multiply: ["$items.price", "$items.quantity"]}}
}},

// 排序
{$sort: {totalAmount: -1}},

// 限制
{$limit: 10}
])

聚合表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 算术运算
$sum, $avg, $min, $max

// 字符串操作
$concat, $substr, $toLower, $toUpper

// 日期操作
$year, $month, $day, $hour

// 条件判断
$cond, $ifNull, $switch

// 数组操作
$size, $slice, $filter

Spring Boot 整合

添加依赖

1
2
3
4
5
<!-- Spring Data MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置文件

1
2
3
4
5
6
7
8
9
10
11
# application.yml
spring:
data:
mongodb:
uri: mongodb://localhost:27017/mydb
# 或者分开配置
# host: localhost
# port: 27017
# database: mydb
# username: user
# password: pass

创建实体类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package com.example.entity;

import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import org.springframework.data.mongodb.core.mapping.Field;

import java.time.LocalDateTime;
import java.util.List;

@Data
@Document(collection = "users")
public class User {

@Id
private String id;

@Field("name")
private String name;

private Integer age;

private String email;

private List<String> hobbies;

private Address address;

private LocalDateTime createTime;

@Data
public static class Address {
private String city;
private String street;
}
}

创建 Repository

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.example.repository;

import com.example.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface UserRepository extends MongoRepository<User, String> {

// 方法名查询
List<User> findByName(String name);

List<User> findByAgeBetween(Integer minAge, Integer maxAge);

List<User> findByEmailContaining(String keyword);

Page<User> findByAgeGreaterThan(Integer age, Pageable pageable);

// 自定义查询
@Query("{'name': ?0, 'age': {$gte: ?1}}")
List<User> findByNameAndAgeGt(String name, Integer age);

// 删除
void deleteByEmail(String email);
}

创建 Service

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package com.example.service;

import com.example.entity.User;
import com.example.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {

@Autowired
private UserRepository userRepository;

@Autowired
private MongoTemplate mongoTemplate;

// 保存文档
public User save(User user) {
return userRepository.save(user);
}

// 批量保存
public List<User> saveAll(List<User> users) {
return userRepository.saveAll(users);
}

// 根据 ID 查询
public User findById(String id) {
return userRepository.findById(id).orElse(null);
}

// 删除文档
public void delete(String id) {
userRepository.deleteById(id);
}

// 分页查询
public Page<User> page(int pageNum, int pageSize) {
return userRepository.findAll(PageRequest.of(pageNum - 1, pageSize));
}

// 条件查询
public List<User> search(String keyword) {
Query query = new Query();
query.addCriteria(
Criteria.where("name").regex(keyword)
.orOperator(Criteria.where("email").regex(keyword))
);
return mongoTemplate.find(query, User.class);
}

// 聚合查询
public List<User> findByAgeRange(Integer minAge, Integer maxAge) {
return userRepository.findByAgeBetween(minAge, maxAge);
}
}

创建 Controller

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package com.example.controller;

import com.example.entity.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/users")
public class UserController {

@Autowired
private UserService userService;

@PostMapping
public User create(@RequestBody User user) {
return userService.save(user);
}

@PostMapping("/batch")
public List<User> batchCreate(@RequestBody List<User> users) {
return userService.saveAll(users);
}

@GetMapping("/{id}")
public User get(@PathVariable String id) {
return userService.findById(id);
}

@DeleteMapping("/{id}")
public void delete(@PathVariable String id) {
userService.delete(id);
}

@GetMapping("/page")
public Page<User> page(
@RequestParam(defaultValue = "1") int pageNum,
@RequestParam(defaultValue = "10") int pageSize) {
return userService.page(pageNum, pageSize);
}

@GetMapping("/search")
public List<User> search(@RequestParam String keyword) {
return userService.search(keyword);
}
}

高级功能

副本集Replica Set

配置副本集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 启动三个 mongod 实例
mongod --replSet rs0 --port 27017 --dbpath /data/rs0-1
mongod --replSet rs0 --port 27018 --dbpath /data/rs0-2
mongod --replSet rs0 --port 27019 --dbpath /data/rs0-3

# 初始化副本集
mongosh --port 27017

rs.initiate({
_id: "rs0",
members: [
{_id: 0, host: "localhost:27017"},
{_id: 1, host: "localhost:27018"},
{_id: 2, host: "localhost:27019"}
]
})

# 查看状态
rs.status()

特点

  • 自动故障转移
  • 数据冗余备份
  • 读写分离支持
  • 至少需要 3 个节点

分片Sharding

配置分片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 启动配置服务器
mongod --configsvr --replSet configReplSet --port 27019

# 启动 mongos 路由
mongos --configdb configReplSet/localhost:27019 --port 27017

# 添加分片
mongosh --port 27017
sh.addShard("shard1/localhost:27001")
sh.addShard("shard2/localhost:27002")

# 启用分片
sh.enableSharding("mydb")

# 创建分片键
sh.shardCollection("mydb.users", {userId: "hashed"})

分片策略

策略 说明 适用场景
范围分片 按范围划分数据 有序数据
哈希分片 按哈希值分布 均匀分布
区域分片 按地理位置分布 地域性数据

GridFS大文件存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 上传文件
@Autowired
private GridFsTemplate gridFsTemplate;

public String uploadFile(MultipartFile file) throws IOException {
GridFSFile gridFSFile = gridFsTemplate.store(
file.getInputStream(),
file.getOriginalFilename(),
file.getContentType()
);
return gridFSFile.getId().toString();
}

// 下载文件
public InputStream downloadFile(String fileId) {
GridFSDBFile gridFSFile = gridFsTemplate.findOne(
Query.query(Criteria.where("_id").is(fileId))
);
return gridFSFile.getInputStream();
}

事务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// MongoDB 4.0+ 支持多文档事务
const session = db.getMongo().startSession();

try {
session.startTransaction();

const usersColl = session.getDatabase("mydb").users;
const ordersColl = session.getDatabase("mydb").orders;

usersColl.insertOne({name: "张三"});
ordersColl.insertOne({userId: "xxx", amount: 100});

session.commitTransaction();
} catch (error) {
session.abortTransaction();
} finally {
session.endSession();
}

性能优化

索引优化

1
2
3
4
5
6
7
8
9
10
11
12
// 1. 创建合适的索引
db.users.createIndex({name: 1, age: 1})

// 2. 使用覆盖索引
db.users.createIndex({email: 1}, {name: "email_idx"})

// 3. 避免索引失效
// 错误db.users.find({$or: [{name: "张三"}, {age: 25}]})
// 正确为每个条件创建索引

// 4. 查看执行计划
db.users.find({name: "张三"}).explain("executionStats")

查询优化

1
2
3
4
5
6
7
8
9
10
11
// 1. 只返回需要的字段
db.users.find({}, {name: 1, age: 1, _id: 0})

// 2. 使用投影减少网络传输
db.users.find({age: {$gt: 20}}, {name: 1})

// 3. 合理使用 limit
db.users.find().limit(100)

// 4. 避免全表扫描
// 确保查询条件有索引

存储优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 1. 使用合适的数据类型
// 错误{age: "25"}
// 正确{age: 25}

// 2. 嵌入 vs 引用
// 一对一或一对少嵌入
{
name: "张三",
address: {city: "北京", street: "长安街"}
}

// 一对多或多对多引用
{
name: "张三",
orderIds: [ObjectId("..."), ObjectId("...")]
}

// 3. 定期清理旧数据
db.logs.createIndex({createdAt: 1}, {expireAfterSeconds: 86400 * 30})

连接池优化

1
2
3
4
5
# application.yml
spring:
data:
mongodb:
uri: mongodb://localhost:27017/mydb?maxPoolSize=100&minPoolSize=10

最佳实践

命名规范

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
数据库命名
- 小写字母
- 使用下划线
- 语义化命名
- 示例user_dborder_system

集合命名
- 复数形式
- 小写字母
- 示例usersordersproducts

字段命名
- 驼峰命名
- 语义化命名
- 示例userNamecreateTime

设计建议

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 1. 合理设计文档结构
// 避免过深嵌套
{
name: "张三",
address: { // 最多 2-3 层
city: "北京",
detail: {street: "长安街"}
}
}

// 2. 控制文档大小
// MongoDB 文档最大 16MB
// 大文件使用 GridFS

// 3. 使用引用处理关系
{
userId: ObjectId("..."),
orderId: ObjectId("...")
}

// 4. 添加审计字段
{
createdAt: ISODate(),
updatedAt: ISODate(),
createdBy: "admin"
}

监控建议

1
2
3
4
5
6
7
8
9
10
11
12
# 1. 监控慢查询
db.setProfilingLevel(1, 100) // 记录超过 100ms 的查询
db.system.profile.find().sort({ts: -1}).limit(10)

# 2. 监控连接数
db.serverStatus().connections

# 3. 监控内存使用
db.serverStatus().mem

# 4. 使用 MongoDB Compass
# 官方可视化工具

常见问题

性能问题

1
2
3
4
5
6
7
8
问题查询速度慢

解决方案
1. 检查是否使用索引
db.collection.find().explain()
2. 创建合适的索引
3. 优化查询条件
4. 使用投影减少返回字段

内存溢出

1
2
3
4
5
6
7
8
9
问题OutOfMemory

解决方案
1. 增加 WiredTiger 缓存
storage.wiredTiger.engineConfig.cacheSizeGB
2. 优化查询减少返回数据量
3. 使用分页
4. 限制聚合管道的内存使用
db.collection.aggregate([...], {allowDiskUse: true})

连接超时

1
2
3
4
5
6
7
问题Connection timeout

解决方案
1. 检查网络连接
2. 增加连接超时时间
3. 使用连接池
4. 检查防火墙设置

报错处理

💗💗 MongoDB 报错E11000 duplicate key error

1
2
3
4
5
6
7
8
9
10
11
错误信息
E11000 duplicate key error collection: mydb.users index: email_1 dup key: { email: "test@example.com" }

错误原因
违反唯一索引约束

解决方案
1. 检查数据是否已存在
2. 删除或修改重复数据
3. 如果不需要唯一性删除唯一索引
db.users.dropIndex("email_1")

💗💗 MongoDB 报错WriteConcernError

1
2
3
4
5
6
7
8
9
10
11
12
错误信息
WriteConcernError: waiting for replication timed out

错误原因
副本集写入确认超时

解决方案
1. 检查副本集状态
rs.status()
2. 调整 WriteConcern
db.collection.insertOne(doc, {writeConcern: {w: 1}})
3. 检查网络连接

💗💗 MongoDB 报错CursorNotFound

1
2
3
4
5
6
7
8
9
10
11
错误信息
CursorNotFound: cursor id xxx not found

错误原因
游标超时或已被关闭

解决方案
1. 增加游标超时时间
db.collection.find().noCursorTimeout()
2. 使用批量处理
3. 避免长时间持有游标

学习资源

  • 视频
    • MongoDB 基础入门到高级进阶https://www.bilibili.com/video/BV1bJ411x7mq
  • 官方文档
    • MongoDB 官方文档https://www.mongodb.com/docs/
    • MongoDB GitHubhttps://github.com/mongodb/mongo
  • 书籍
    • MongoDB 权威指南Kristina Chodorow 著
    • MongoDB 实战Kyle Banker 著
  • 教程
    • MongoDB 入门教程https://www.runoob.com/mongodb/mongodb-tutorial.html
    • MongoDB Universityhttps://university.mongodb.com/
  • 工具
    • MongoDB Compass官方可视化工具
    • Robo 3T免费管理工具
    • Studio 3T专业管理工具
  • 社区
    • MongoDB 中文社区https://mongoing.com/
    • Stack Overflow MongoDB 标签https://stackoverflow.com/questions/tagged/mongodb