Skip to content

MongoDB 教程 ​

MongoDB 是一个基于分布式文件存储的开源 NoSQL 数据库系统,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。本教程将帮助你掌握 MongoDB 的基础知识和实用技能。

MongoDB 简介 ​

什么是 MongoDB? ​

MongoDB 是一个面向文档的数据库,它将数据存储在类似 JSON 的文档中,而不是传统关系数据库的表格中。这种灵活的数据模型使得 MongoDB 能够存储结构复杂的数据,并且可以随着应用需求的变化而轻松调整数据模式。

MongoDB 的主要特点 ​

  • 文档数据模型:数据以 BSON(二进制 JSON)格式存储,支持嵌套、数组等复杂结构
  • 高性能:支持索引、分片、复制集,提供高吞吐量和低延迟
  • 高可用性:通过复制集实现数据冗余和自动故障转移
  • 水平扩展:通过分片实现数据分布式存储,支持大规模数据集
  • 灵活的查询语言:强大的查询功能,支持聚合、全文搜索等
  • 无模式设计:集合中的文档可以有不同的字段,无需预定义结构

MongoDB 与关系型数据库的比较 ​

SQL 术语/概念MongoDB 术语/概念
数据库 (Database)数据库 (Database)
表 (Table)集合 (Collection)
行 (Row)文档 (Document)
列 (Column)字段 (Field)
主键 (Primary Key)_id 字段
索引 (Index)索引 (Index)
表连接 (Table Joins)$lookup, 嵌入文档
外键 (Foreign Key)引用 (References)

安装 MongoDB ​

Windows 安装 ​

  1. 访问 MongoDB 下载页面
  2. 选择 Windows 版本并下载 MSI 安装包
  3. 运行安装程序,按照向导完成安装
  4. 可选择安装 MongoDB Compass(图形化管理工具)
  5. 安装完成后,MongoDB 服务会自动启动

macOS 安装 ​

使用 Homebrew 安装:

bash
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装 MongoDB
brew tap mongodb/brew
brew install mongodb-community

# 启动 MongoDB 服务
brew services start mongodb-community

Linux 安装 (Ubuntu) ​

bash
# 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -

# 创建源列表文件
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list

# 更新包数据库
sudo apt-get update

# 安装 MongoDB
sudo apt-get install -y mongodb-org

# 启动 MongoDB 服务
sudo systemctl start mongod

# 设置开机自启
sudo systemctl enable mongod

Docker 安装 ​

bash
# 拉取 MongoDB 镜像
docker pull mongo:latest

# 运行 MongoDB 容器
docker run -d --name mongodb -p 27017:27017 -v mongodb_data:/data/db mongo:latest

验证安装 ​

安装完成后,可以通过以下命令验证 MongoDB 是否正常运行:

bash
# 连接到 MongoDB Shell
mongo
# 或使用新版客户端
mongosh

# 显示版本信息
db.version()

MongoDB 基本概念 ​

数据库、集合和文档 ​

  • 数据库 (Database):MongoDB 中的数据库是集合的容器,一个 MongoDB 服务器可以有多个数据库
  • 集合 (Collection):集合是 MongoDB 文档的分组,类似于关系数据库中的表
  • 文档 (Document):文档是 MongoDB 中的基本数据单元,由键值对组成,类似于 JSON 对象

文档结构 ​

MongoDB 文档使用 BSON(Binary JSON)格式存储,示例如下:

json
{
  "_id": ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
  "name": "张三",
  "age": 30,
  "email": "zhangsan@example.com",
  "address": {
    "city": "北京",
    "postcode": "100000"
  },
  "tags": ["开发", "设计", "数据库"],
  "active": true,
  "created_at": ISODate("2021-05-20T12:30:45.000Z")
}

数据类型 ​

MongoDB 支持多种数据类型:

  • String:字符串,UTF-8 编码
  • Integer:32 位或 64 位整数
  • Boolean:true 或 false
  • Double:浮点数
  • Array:值的列表或数组
  • Object:嵌入式文档
  • ObjectId:12 字节的唯一标识符,常用作 _id
  • Date:日期时间,以 UNIX 时间格式存储
  • Null:空值
  • Regular Expression:正则表达式
  • Binary Data:二进制数据
  • Code:JavaScript 代码

MongoDB Shell 基本操作 ​

连接数据库 ​

bash
# 连接本地 MongoDB 服务器
mongo
# 或使用新版客户端
mongosh

# 连接远程 MongoDB 服务器
mongo mongodb://username:password@hostname:port/database
# 或使用新版客户端
mongosh mongodb://username:password@hostname:port/database

数据库操作 ​

javascript
// 显示所有数据库
show dbs

// 使用/创建数据库(如果不存在)
use mydb

// 显示当前数据库
db

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

集合操作 ​

javascript
// 创建集合
db.createCollection("users")

// 显示所有集合
show collections

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

文档操作 ​

插入文档 ​

javascript
// 插入单个文档
db.users.insertOne({
  name: "张三",
  age: 30,
  email: "zhangsan@example.com"
})

// 插入多个文档
db.users.insertMany([
  { name: "李四", age: 25, email: "lisi@example.com" },
  { name: "王五", age: 35, email: "wangwu@example.com" }
])

查询文档 ​

javascript
// 查询所有文档
db.users.find()

// 格式化显示结果
db.users.find().pretty()

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

// 使用查询操作符
db.users.find({ age: { $gt: 25 } })  // 大于 25 岁

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

// OR 条件
db.users.find({ $or: [{ age: 30 }, { name: "李四" }] })

// 投影(只返回指定字段)
db.users.find({}, { name: 1, email: 1, _id: 0 })

// 限制结果数量
db.users.find().limit(2)

// 跳过结果
db.users.find().skip(1)

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

// 统计文档数量
db.users.countDocuments({ age: { $gt: 25 } })

更新文档 ​

javascript
// 更新单个文档
db.users.updateOne(
  { name: "张三" },
  { $set: { age: 31, "address.city": "上海" } }
)

// 更新多个文档
db.users.updateMany(
  { age: { $lt: 30 } },
  { $inc: { age: 1 } }
)

// 替换整个文档
db.users.replaceOne(
  { name: "张三" },
  { name: "张三", age: 32, email: "zhangsan@example.com" }
)

删除文档 ​

javascript
// 删除单个文档
db.users.deleteOne({ name: "张三" })

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

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

高级查询 ​

查询操作符 ​

javascript
// 比较操作符
db.users.find({ age: { $eq: 30 } })  // 等于
db.users.find({ age: { $ne: 30 } })  // 不等于
db.users.find({ age: { $gt: 30 } })  // 大于
db.users.find({ age: { $gte: 30 } }) // 大于等于
db.users.find({ age: { $lt: 30 } })  // 小于
db.users.find({ age: { $lte: 30 } }) // 小于等于
db.users.find({ age: { $in: [25, 30, 35] } }) // 在指定数组中
db.users.find({ age: { $nin: [25, 30] } })    // 不在指定数组中

// 逻辑操作符
db.users.find({ $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] })
db.users.find({ $or: [{ age: 25 }, { age: 35 }] })
db.users.find({ age: { $not: { $gt: 30 } } })
db.users.find({ $nor: [{ age: 25 }, { age: 35 }] })

// 元素操作符
db.users.find({ age: { $exists: true } })  // 字段存在
db.users.find({ age: { $type: "number" } }) // 字段类型

// 数组操作符
db.users.find({ tags: { $all: ["开发", "设计"] } })  // 包含所有指定元素
db.users.find({ tags: { $size: 3 } })               // 数组大小
db.users.find({ tags: { $elemMatch: { $eq: "开发" } } }) // 数组元素匹配

正则表达式查询 ​

javascript
// 使用正则表达式查询
db.users.find({ name: /^张/ })  // 以"张"开头的名字
db.users.find({ email: /example\.com$/ }) // 以"example.com"结尾的邮箱

嵌套文档查询 ​

javascript
// 精确匹配嵌套文档
db.users.find({ address: { city: "北京", postcode: "100000" } })

// 查询嵌套字段
db.users.find({ "address.city": "北京" })

数组查询 ​

javascript
// 精确匹配数组
db.users.find({ tags: ["开发", "设计", "数据库"] })

// 查询数组包含元素
db.users.find({ tags: "开发" })

// 查询数组中的特定元素
db.users.find({ "tags.0": "开发" })  // 第一个元素是"开发"

索引 ​

创建索引 ​

javascript
// 创建单字段索引
db.users.createIndex({ name: 1 })  // 1 表示升序索引,-1 表示降序索引

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

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

// 创建 TTL 索引(文档自动过期)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

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

// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" })

查看索引 ​

javascript
// 查看集合的所有索引
db.users.getIndexes()

删除索引 ​

javascript
// 删除特定索引
db.users.dropIndex("name_1")

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

聚合操作 ​

MongoDB 的聚合框架提供了强大的数据处理能力,可以对文档进行转换和组合。

聚合管道 ​

javascript
// 基本聚合示例
db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$customer_id", totalAmount: { $sum: "$amount" } } },
  { $sort: { totalAmount: -1 } }
])

常用聚合操作符 ​

javascript
// $match:筛选文档
db.users.aggregate([
  { $match: { age: { $gt: 25 } } }
])

// $group:分组
db.users.aggregate([
  { $group: { _id: "$age", count: { $sum: 1 } } }
])

// $project:投影
db.users.aggregate([
  { $project: { name: 1, email: 1, _id: 0 } }
])

// $sort:排序
db.users.aggregate([
  { $sort: { age: -1 } }
])

// $limit:限制结果数量
db.users.aggregate([
  { $limit: 5 }
])

// $skip:跳过结果
db.users.aggregate([
  { $skip: 5 }
])

// $unwind:展开数组
db.users.aggregate([
  { $unwind: "$tags" }
])

// $lookup:连接操作(类似 SQL JOIN)
db.orders.aggregate([
  {
    $lookup: {
      from: "customers",
      localField: "customer_id",
      foreignField: "_id",
      as: "customer_info"
    }
  }
])

聚合表达式 ​

javascript
// 算术表达式
db.products.aggregate([
  {
    $project: {
      name: 1,
      discountPrice: { $multiply: ["$price", 0.9] }
    }
  }
])

// 日期表达式
db.sales.aggregate([
  {
    $project: {
      year: { $year: "$date" },
      month: { $month: "$date" },
      day: { $dayOfMonth: "$date" }
    }
  }
])

// 字符串表达式
db.users.aggregate([
  {
    $project: {
      fullName: { $concat: ["$firstName", " ", "$lastName"] },
      nameLength: { $strLenCP: "$name" }
    }
  }
])

// 条件表达式
db.users.aggregate([
  {
    $project: {
      category: {
        $cond: { if: { $gte: ["$age", 30] }, then: "成年人", else: "青年" }
      }
    }
  }
])

事务 ​

从 MongoDB 4.0 开始,支持多文档事务,允许对多个文档的操作作为一个单元执行。

事务示例 ​

javascript
// 启动会话
const session = db.getMongo().startSession();

// 开始事务
session.startTransaction();

try {
  // 执行操作
  const usersCollection = session.getDatabase("mydb").getCollection("users");
  const ordersCollection = session.getDatabase("mydb").getCollection("orders");
  
  usersCollection.updateOne(
    { _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a") },
    { $inc: { balance: -100 } }
  );
  
  ordersCollection.insertOne({
    user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
    amount: 100,
    status: "completed"
  });
  
  // 提交事务
  session.commitTransaction();
} catch (error) {
  // 出错时回滚事务
  session.abortTransaction();
  print("事务失败:", error);
} finally {
  // 结束会话
  session.endSession();
}

数据建模 ​

嵌入式数据模型 ​

嵌入式数据模型将相关数据嵌入到单个文档中,适合一对一和一对多关系。

javascript
// 嵌入式数据模型示例
db.users.insertOne({
  name: "张三",
  email: "zhangsan@example.com",
  address: {
    street: "中关村大街",
    city: "北京",
    postcode: "100000"
  },
  orders: [
    { id: "ORD001", amount: 100, date: ISODate("2021-05-20") },
    { id: "ORD002", amount: 200, date: ISODate("2021-05-25") }
  ]
})

引用式数据模型 ​

引用式数据模型通过引用(类似外键)连接相关文档,适合多对多关系或大型嵌套文档。

javascript
// 引用式数据模型示例
db.users.insertOne({
  _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
  name: "张三",
  email: "zhangsan@example.com"
})

db.orders.insertMany([
  {
    _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7b"),
    user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
    amount: 100,
    date: ISODate("2021-05-20")
  },
  {
    _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7c"),
    user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
    amount: 200,
    date: ISODate("2021-05-25")
  }
])

数据模型设计考虑因素 ​

  1. 读写比例:读操作多的场景倾向于嵌入式模型,写操作多的场景倾向于引用式模型
  2. 数据一致性:嵌入式模型提供文档级别的原子性
  3. 数据增长:如果嵌入文档可能无限增长,考虑使用引用式模型
  4. 数据访问模式:考虑数据如何被查询和更新
  5. 文档大小限制:MongoDB 文档大小限制为 16MB

MongoDB 安全 ​

启用身份验证 ​

javascript
// 创建管理员用户
use admin
db.createUser({
  user: "adminUser",
  pwd: "securePassword",
  roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})

// 创建数据库用户
use mydb
db.createUser({
  user: "appUser",
  pwd: "appPassword",
  roles: [{ role: "readWrite", db: "mydb" }]
})

配置文件启用身份验证 ​

在 MongoDB 配置文件中添加:

yaml
security:
  authorization: enabled

角色管理 ​

MongoDB 内置角色:

  • read:允许读取指定数据库中的任何数据
  • readWrite:允许读写指定数据库中的任何数据
  • dbAdmin:允许在指定数据库上执行管理操作
  • userAdmin:允许在指定数据库上创建和修改用户
  • clusterAdmin:允许对整个集群执行管理操作
  • readAnyDatabase:允许读取所有数据库
  • readWriteAnyDatabase:允许读写所有数据库
  • userAdminAnyDatabase:允许在所有数据库上创建和修改用户
  • dbAdminAnyDatabase:允许在所有数据库上执行管理操作
  • root:超级用户,拥有所有权限

网络安全 ​

  1. 绑定 IP 地址:限制 MongoDB 只接受特定 IP 地址的连接
  2. 使用 TLS/SSL:加密客户端和服务器之间的通信
  3. 使用 VPN 或专用网络:将 MongoDB 部署在安全的网络环境中

MongoDB 复制集 ​

复制集是一组维护相同数据集的 MongoDB 服务器,提供冗余和高可用性。

复制集架构 ​

典型的复制集包含:

  • 一个主节点(Primary):接收所有写操作
  • 多个从节点(Secondary):复制主节点的数据
  • 可选的仲裁节点(Arbiter):参与选举但不存储数据

设置复制集 ​

  1. 启动多个 MongoDB 实例:
bash
# 启动第一个实例
mongod --replSet "rs0" --port 27017 --dbpath /data/db1

# 启动第二个实例
mongod --replSet "rs0" --port 27018 --dbpath /data/db2

# 启动第三个实例
mongod --replSet "rs0" --port 27019 --dbpath /data/db3
  1. 初始化复制集:
javascript
// 连接到其中一个实例
mongo --port 27017

// 初始化复制集
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
})

// 查看复制集状态
rs.status()

复制集操作 ​

javascript
// 查看复制集配置
rs.conf()

// 添加成员
rs.add("localhost:27020")

// 添加仲裁节点
rs.addArb("localhost:27021")

// 删除成员
rs.remove("localhost:27020")

// 强制重新选举
rs.stepDown()

MongoDB 分片 ​

分片是 MongoDB 实现水平扩展的方法,通过将数据分布到多个服务器上来支持大型数据集和高吞吐量操作。

分片架构 ​

MongoDB 分片集群包含:

  • 分片(Shard):存储数据的 MongoDB 实例或复制集
  • 配置服务器(Config Server):存储集群元数据
  • 路由服务(mongos):路由客户端请求到适当的分片

分片键选择 ​

选择分片键时需要考虑:

  • 基数:高基数的字段(如 UUID)分布更均匀
  • 写分布:避免写操作集中在少数分片
  • 查询隔离:常用查询应该只需访问少数分片
  • 数据增长:考虑数据如何随时间增长

MongoDB Atlas ​

MongoDB Atlas 是 MongoDB 官方提供的数据库即服务(DBaaS)解决方案,提供自动化部署、扩展和管理 MongoDB 数据库的功能。

Atlas 主要特点 ​

  • 全托管服务:无需管理服务器、备份或安全
  • 多云支持:可在 AWS、Azure 或 GCP 上部署
  • 自动扩展:根据需求自动调整资源
  • 内置监控:实时性能监控和警报
  • 自动备份:定期备份和时间点恢复
  • 高级安全:网络隔离、加密、访问控制

MongoDB 与应用程序集成 ​

Node.js 集成 ​

使用官方 MongoDB Node.js 驱动程序:

javascript
// 安装驱动程序
// npm install mongodb

// 连接到 MongoDB
const { MongoClient } = require('mongodb');

const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);

async function run() {
  try {
    await client.connect();
    const database = client.db("mydb");
    const users = database.collection("users");
    
    // 插入文档
    const insertResult = await users.insertOne({
      name: "张三",
      age: 30,
      email: "zhangsan@example.com"
    });
    console.log(`插入的文档 ID: ${insertResult.insertedId}`);
    
    // 查询文档
    const query = { age: { $gt: 25 } };
    const cursor = users.find(query);
    await cursor.forEach(doc => {
      console.log(doc);
    });
    
  } finally {
    await client.close();
  }
}

run().catch(console.dir);

Python 集成 ​

使用 PyMongo 驱动程序:

python
# 安装驱动程序
# pip install pymongo

from pymongo import MongoClient

# 连接到 MongoDB
client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
users = db['users']

# 插入文档
user_data = {
    "name": "张三",
    "age": 30,
    "email": "zhangsan@example.com"
}
result = users.insert_one(user_data)
print(f"插入的文档 ID: {result.inserted_id}")

# 查询文档
for user in users.find({"age": {"$gt": 25}}):
    print(user)

Java 集成 ​

使用 MongoDB Java 驱动程序:

java
// 添加依赖
// implementation 'org.mongodb:mongodb-driver-sync:4.3.0'

import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;

public class MongoDBExample {
    public static void main(String[] args) {
        // 连接到 MongoDB
        try (MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017")) {
            MongoDatabase database = mongoClient.getDatabase("mydb");
            MongoCollection<Document> collection = database.getCollection("users");
            
            // 插入文档
            Document doc = new Document("name", "张三")
                    .append("age", 30)
                    .append("email", "zhangsan@example.com");
            collection.insertOne(doc);
            System.out.println("文档插入成功");
            
            // 查询文档
            Document query = new Document("age", new Document("$gt", 25));
            collection.find(query).forEach(document -> {
                System.out.println(document.toJson());
            });
        }
    }
}

MongoDB 最佳实践 ​

性能优化 ​

  1. 使用适当的索引:为常用查询创建索引,但避免过多索引
  2. 批量操作:使用批量插入、更新和删除操作
  3. 投影:只返回需要的字段,减少网络传输
  4. 限制结果集大小:使用 limit() 和分页
  5. 使用覆盖查询:查询只包含索引字段
  6. 避免大型文档:保持文档大小合理,避免接近 16MB 限制
  7. 使用适当的读写关注:根据应用需求选择合适的读写关注级别

数据库管理 ​

  1. 定期备份:实施定期备份策略
  2. 监控数据库性能:使用 MongoDB Atlas 或其他监控工具
  3. 计划容量:预测数据增长并相应扩展
  4. 实施适当的安全措施:身份验证、授权和加密
  5. 保持 MongoDB 版本更新:定期升级到最新稳定版本

常见问题解决 ​

  1. 慢查询:使用 explain() 分析查询性能,创建适当的索引
  2. 内存使用:确保 MongoDB 有足够的内存,避免频繁的页面交换
  3. 连接问题:检查网络配置、防火墙规则和身份验证设置
  4. 数据一致性:使用适当的写关注级别和事务
  5. 磁盘空间:监控磁盘使用情况,及时清理不需要的数据

相关资源 ​


本文档将持续更新,如有问题请通过 GitHub Issues 反馈。

MongoDB 基本概念 ​

数据库、集合和文档 ​

  • 数据库 (Database):MongoDB 中的数据库是集合的容器,一个 MongoDB 服务器可以有多个数据库
  • 集合 (Collection):集合是 MongoDB 文档的分组,类似于关系数据库中的表
  • 文档 (Document):文档是 MongoDB 中的基本数据单元,由键值对组成,类似于 JSON 对象

文档结构 ​

MongoDB 文档使用 BSON(Binary JSON)格式存储,示例如下:

json
{
  "_id": ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
  "name": "张三",
  "age": 30,
  "email": "zhangsan@example.com",
  "address": {
    "city": "北京",
    "postcode": "100000"
  },
  "tags": ["开发", "设计", "数据库"],
  "active": true,
  "created_at": ISODate("2021-05-20T12:30:45.000Z")
}

数据类型 ​

MongoDB 支持多种数据类型:

  • String:字符串,UTF-8 编码
  • Integer:32 位或 64 位整数
  • Boolean:true 或 false
  • Double:浮点数
  • Array:值的列表或数组
  • Object:嵌入式文档
  • ObjectId:12 字节的唯一标识符,常用作 _id
  • Date:日期时间,以 UNIX 时间格式存储
  • Null:空值
  • Regular Expression:正则表达式
  • Binary Data:二进制数据
  • Code:JavaScript 代码

MongoDB Shell 基本操作 ​

连接数据库 ​

bash
# 连接本地 MongoDB 服务器
mongo
# 或使用新版客户端
mongosh

# 连接远程 MongoDB 服务器
mongo mongodb://username:password@hostname:port/database
# 或使用新版客户端
mongosh mongodb://username:password@hostname:port/database

数据库操作 ​

javascript
// 显示所有数据库
show dbs

// 使用/创建数据库(如果不存在)
use mydb

// 显示当前数据库
db

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

集合操作 ​

javascript
// 创建集合
db.createCollection("users")

// 显示所有集合
show collections

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

文档操作 ​

插入文档 ​

javascript
// 插入单个文档
db.users.insertOne({
  name: "张三",
  age: 30,
  email: "zhangsan@example.com"
})

// 插入多个文档
db.users.insertMany([
  { name: "李四", age: 25, email: "lisi@example.com" },
  { name: "王五", age: 35, email: "wangwu@example.com" }
])

查询文档 ​

javascript
// 查询所有文档
db.users.find()

// 格式化显示结果
db.users.find().pretty()

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

// 使用查询操作符
db.users.find({ age: { $gt: 25 } })  // 大于 25 岁

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

// OR 条件
db.users.find({ $or: [{ age: 30 }, { name: "李四" }] })

// 投影(只返回指定字段)
db.users.find({}, { name: 1, email: 1, _id: 0 })

// 限制结果数量
db.users.find().limit(2)

// 跳过结果
db.users.find().skip(1)

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

// 统计文档数量
db.users.countDocuments({ age: { $gt: 25 } })

更新文档 ​

javascript
// 更新单个文档
db.users.updateOne(
  { name: "张三" },
  { $set: { age: 31, "address.city": "上海" } }
)

// 更新多个文档
db.users.updateMany(
  { age: { $lt: 30 } },
  { $inc: { age: 1 } }
)

// 替换整个文档
db.users.replaceOne(
  { name: "张三" },
  { name: "张三", age: 32, email: "zhangsan@example.com" }
)

删除文档 ​

javascript
// 删除单个文档
db.users.deleteOne({ name: "张三" })

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

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

高级查询 ​

查询操作符 ​

javascript
// 比较操作符
db.users.find({ age: { $eq: 30 } })  // 等于
db.users.find({ age: { $ne: 30 } })  // 不等于
db.users.find({ age: { $gt: 30 } })  // 大于
db.users.find({ age: { $gte: 30 } }) // 大于等于
db.users.find({ age: { $lt: 30 } })  // 小于
db.users.find({ age: { $lte: 30 } }) // 小于等于
db.users.find({ age: { $in: [25, 30, 35] } }) // 在指定数组中
db.users.find({ age: { $nin: [25, 30] } })    // 不在指定数组中

// 逻辑操作符
db.users.find({ $and: [{ age: { $gt: 25 } }, { age: { $lt: 35 } }] })
db.users.find({ $or: [{ age: 25 }, { age: 35 }] })
db.users.find({ age: { $not: { $gt: 30 } } })
db.users.find({ $nor: [{ age: 25 }, { age: 35 }] })

// 元素操作符
db.users.find({ age: { $exists: true } })  // 字段存在
db.users.find({ age: { $type: "number" } }) // 字段类型

// 数组操作符
db.users.find({ tags: { $all: ["开发", "设计"] } })  // 包含所有指定元素
db.users.find({ tags: { $size: 3 } })               // 数组大小
db.users.find({ tags: { $elemMatch: { $eq: "开发" } } }) // 数组元素匹配

正则表达式查询 ​

javascript
// 使用正则表达式查询
db.users.find({ name: /^张/ })  // 以"张"开头的名字
db.users.find({ email: /example\.com$/ }) // 以"example.com"结尾的邮箱

嵌套文档查询 ​

javascript
// 精确匹配嵌套文档
db.users.find({ address: { city: "北京", postcode: "100000" } })

// 查询嵌套字段
db.users.find({ "address.city": "北京" })

数组查询 ​

javascript
// 精确匹配数组
db.users.find({ tags: ["开发", "设计", "数据库"] })

// 查询数组包含元素
db.users.find({ tags: "开发" })

// 查询数组中的特定元素
db.users.find({ "tags.0": "开发" })  // 第一个元素是"开发"

索引 ​

创建索引 ​

javascript
// 创建单字段索引
db.users.createIndex({ name: 1 })  // 1 表示升序索引,-1 表示降序索引

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

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

// 创建 TTL 索引(文档自动过期)
db.sessions.createIndex({ createdAt: 1 }, { expireAfterSeconds: 3600 })

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

// 创建地理空间索引
db.places.createIndex({ location: "2dsphere" })

查看索引 ​

javascript
// 查看集合的所有索引
db.users.getIndexes()

删除索引 ​

javascript
// 删除特定索引
db.users.dropIndex("name_1")

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

聚合操作 ​

MongoDB 的聚合框架提供了强大的数据处理能力,可以对文档进行转换和组合。

聚合管道 ​

javascript
// 基本聚合示例
db.orders.aggregate([
  { $match: { status: "completed" } },
  { $group: { _id: "$customer_id", totalAmount: { $sum: "$amount" } } },
  { $sort: { totalAmount: -1 } }
])

常用聚合操作符 ​

javascript
// $match:筛选文档
db.users.aggregate([
  { $match: { age: { $gt: 25 } } }
])

// $group:分组
db.users.aggregate([
  { $group: { _id: "$age", count: { $sum: 1 } } }
])

// $project:投影
db.users.aggregate([
  { $project: { name: 1, email: 1, _id: 0 } }
])

// $sort:排序
db.users.aggregate([
  { $sort: { age: -1 } }
])

// $limit:限制结果数量
db.users.aggregate([
  { $limit: 5 }
])

// $skip:跳过结果
db.users.aggregate([
  { $skip: 5 }
])

// $unwind:展开数组
db.users.aggregate([
  { $unwind: "$tags" }
])

// $lookup:连接操作(类似 SQL JOIN)
db.orders.aggregate([
  {
    $lookup: {
      from: "customers",
      localField: "customer_id",
      foreignField: "_id",
      as: "customer_info"
    }
  }
])

聚合表达式 ​

javascript
// 算术表达式
db.products.aggregate([
  {
    $project: {
      name: 1,
      discountPrice: { $multiply: ["$price", 0.9] }
    }
  }
])

// 日期表达式
db.sales.aggregate([
  {
    $project: {
      year: { $year: "$date" },
      month: { $month: "$date" },
      day: { $dayOfMonth: "$date" }
    }
  }
])

// 字符串表达式
db.users.aggregate([
  {
    $project: {
      fullName: { $concat: ["$firstName", " ", "$lastName"] },
      nameLength: { $strLenCP: "$name" }
    }
  }
])

// 条件表达式
db.users.aggregate([
  {
    $project: {
      category: {
        $cond: { if: { $gte: ["$age", 30] }, then: "成年人", else: "青年" }
      }
    }
  }
])

事务 ​

从 MongoDB 4.0 开始,支持多文档事务,允许对多个文档的操作作为一个单元执行。

事务示例 ​

javascript
// 启动会话
const session = db.getMongo().startSession();

// 开始事务
session.startTransaction();

try {
  // 执行操作
  const usersCollection = session.getDatabase("mydb").getCollection("users");
  const ordersCollection = session.getDatabase("mydb").getCollection("orders");
  
  usersCollection.updateOne(
    { _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a") },
    { $inc: { balance: -100 } }
  );
  
  ordersCollection.insertOne({
    user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
    amount: 100,
    status: "completed"
  });
  
  // 提交事务
  session.commitTransaction();
} catch (error) {
  // 出错时回滚事务
  session.abortTransaction();
  print("事务失败:", error);
} finally {
  // 结束会话
  session.endSession();
}

数据建模 ​

嵌入式数据模型 ​

嵌入式数据模型将相关数据嵌入到单个文档中,适合一对一和一对多关系。

javascript
// 嵌入式数据模型示例
db.users.insertOne({
  name: "张三",
  email: "zhangsan@example.com",
  address: {
    street: "中关村大街",
    city: "北京",
    postcode: "100000"
  },
  orders: [
    { id: "ORD001", amount: 100, date: ISODate("2021-05-20") },
    { id: "ORD002", amount: 200, date: ISODate("2021-05-25") }
  ]
})

引用式数据模型 ​

引用式数据模型通过引用(类似外键)连接相关文档,适合多对多关系或大型嵌套文档。

javascript
// 引用式数据模型示例
db.users.insertOne({
  _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
  name: "张三",
  email: "zhangsan@example.com"
})

db.orders.insertMany([
  {
    _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7b"),
    user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
    amount: 100,
    date: ISODate("2021-05-20")
  },
  {
    _id: ObjectId("60a6e5e91d0d7f2e8c3e4b7c"),
    user_id: ObjectId("60a6e5e91d0d7f2e8c3e4b7a"),
    amount: 200,
    date: ISODate("2021-05-25")
  }
])

数据模型设计考虑因素 ​

  1. 读写比例:读操作多的场景倾向于嵌入式模型,写操作多的场景倾向于引用式模型
  2. 数据一致性:嵌入式模型提供文档级别的原子性
  3. 数据增长:如果嵌入文档可能无限增长,考虑使用引用式模型
  4. 数据访问模式:考虑数据如何被查询和更新
  5. 文档大小限制:MongoDB 文档大小限制为 16MB

MongoDB 安全 ​

启用身份验证 ​

javascript
// 创建管理员用户
use admin
db.createUser({
  user: "adminUser",
  pwd: "securePassword",
  roles: [{ role: "userAdminAnyDatabase", db: "admin" }]
})

// 创建数据库用户
use mydb
db.createUser({
  user: "appUser",
  pwd: "appPassword",
  roles: [{ role: "readWrite", db: "mydb" }]
})

配置文件启用身份验证 ​

在 MongoDB 配置文件中添加:

yaml
security:
  authorization: enabled

角色管理 ​

MongoDB 内置角色:

  • read:允许读取指定数据库中的任何数据
  • readWrite:允许读写指定数据库中的任何数据
  • dbAdmin:允许在指定数据库上执行管理操作
  • userAdmin:允许在指定数据库上创建和修改用户
  • clusterAdmin:允许对整个集群执行管理操作
  • readAnyDatabase:允许读取所有数据库
  • readWriteAnyDatabase:允许读写所有数据库
  • userAdminAnyDatabase:允许在所有数据库上创建和修改用户
  • dbAdminAnyDatabase:允许在所有数据库上执行管理操作
  • root:超级用户,拥有所有权限

网络安全 ​

  1. 绑定 IP 地址:限制 MongoDB 只接受特定 IP 地址的连接
  2. 使用 TLS/SSL:加密客户端和服务器之间的通信
  3. 使用 VPN 或专用网络:将 MongoDB 部署在安全的网络环境中

MongoDB 复制集 ​

复制集是一组维护相同数据集的 MongoDB 服务器,提供冗余和高可用性。

复制集架构 ​

典型的复制集包含:

  • 一个主节点(Primary):接收所有写操作
  • 多个从节点(Secondary):复制主节点的数据
  • 可选的仲裁节点(Arbiter):参与选举但不存储数据

设置复制集 ​

  1. 启动多个 MongoDB 实例:
bash
# 启动第一个实例
mongod --replSet "rs0" --port 27017 --dbpath /data/db1

# 启动第二个实例
mongod --replSet "rs0" --port 27018 --dbpath /data/db2

# 启动第三个实例
mongod --replSet "rs0" --port 27019 --dbpath /data/db3
  1. 初始化复制集:
javascript
// 连接到其中一个实例
mongo --port 27017

// 初始化复制集
rs.initiate({
  _id: "rs0",
  members: [
    { _id: 0, host: "localhost:27017" },
    { _id: 1, host: "localhost:27018" },
    { _id: 2, host: "localhost:27019" }
  ]
})

// 查看复制集状态
rs.status()

复制集操作 ​

javascript
// 查看复制集配置
rs.conf()

// 添加成员
rs.add("localhost:27020")

// 添加仲裁节点
rs.addArb("localhost:27021")

// 删除成员
rs.remove("localhost:27020")

// 强制重新选举
rs.stepDown()

MongoDB 分片 ​

分片是 MongoDB 实现水平扩展的方法,通过将数据分布到多个服务器上来支持大型数据集和高吞吐量操作。

分片架构 ​

MongoDB 分片集群包含:

  • 分片(Shard):存储数据的 MongoDB 实例或复制集
  • 配置服务器(Config Server):存储集群元数据
  • 路由服务(mongos):路由客户端请求到适当的分片

设置分片集群 ​

设置分片集群的步骤较为复杂,通常包括:

  1. 设置配置服务器复制集
  2. 设置多个分片(每个分片通常是一个复制集)
  3. 设置 mongos 路由服务
  4. 添加分片到集群
  5. 启用数据库分片
  6. 对集合进行分片

分片键选择 ​

选择分片键时需要考虑:

  • 基数:高基数的字段(如 UUID)分布更均匀
  • 写分布:避免写操作集中在少数分片
  • 查询隔离:常用查询应该只需访问少数分片
  • 数据增长:考虑数据如何随时间增长

MongoDB Atlas ​

MongoDB Atlas 是 MongoDB 官方提供的数据库即服务(DBaaS)解决方案,提供自动化部署、扩展和管理 MongoDB 数据库的功能。

Atlas 主要特点 ​

  • 全托管服务:无需管理服务器、备份或安全
  • 多云支持:可在 AWS、Azure 或 GCP 上部署
  • 自动扩展:根据需求自动调整资源
  • 内置监控:实时性能监控和警报
  • 自动备份:定期备份和时间点恢复
  • 高级安全:网络隔离、加密、访问控制

使用 Atlas 的步骤 ​

  1. 创建 MongoDB Atlas 账户
  2. 创建新集群
  3. 配置网络访问和数据库用户
  4. 连接到集群
  5. 导入数据并开始使用

MongoDB 与应用程序集成 ​

Node.js 集成 ​

使用官方 MongoDB Node.js 驱动程序:

javascript
// 安装驱动程序
// npm install mongodb

// 连接到 MongoDB
const { MongoClient } = require('mongodb');

const uri = "mongodb://localhost:27017";
const client = new MongoClient(uri);

async function run() {
  try {
    await client.connect();
    const database = client.db("mydb");
    const users = database.collection("users");
    
    // 插入文档
    const insertResult = await users.insertOne({
      name: "张三",
      age: 30,
      email: "zhangsan@example.com"
    });
    console.log(`插入的文档 ID: ${insertResult.insertedId}`);
    
    // 查询文档
    const query = { age: { $gt: 25 } };
    const cursor = users.find(query);
    await cursor.forEach(doc => {
      console.log(doc);
    });
    
  } finally {
    await client.close();
  }
}

run().catch(console.dir);

Python 集成 ​

使用 PyMongo 驱动程序:

python
# 安装驱动程序
# pip install pymongo

from pymongo import MongoClient

# 连接到 MongoDB
client = MongoClient('mongodb://localhost:27017/')
db = client['mydb']
users = db['users']

# 插入文档
user_data = {
    "name": "张三",
    "age": 30,
    "email": "zhangsan@example.com"
}
result = users.insert_one(user_data)
print(f"插入的文档 ID: {result.inserted_id}")

# 查询文档
for user in users.find({"age": {"$gt": 25}}):
    print(user)

Java 集成 ​

使用 MongoDB Java 驱动程序:

java
// 添加依赖
// implementation 'org.mongodb:mongodb-driver-sync:4.3.0'

import com.mongodb.client.MongoClients;
import com.mongodb.client.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import org.bson.Document;

public class MongoDBExample {
    public static void main(String[] args) {
        // 连接到 MongoDB
        try (MongoClient mongoClient = MongoClients.create("mongodb://localhost:27017")) {
            MongoDatabase database = mongoClient.getDatabase("mydb");
            MongoCollection<Document> collection = database.getCollection("users");
            
            // 插入文档
            Document doc = new Document("name", "张三")
                    .append("age", 30)
                    .append("email", "zhangsan@example.com");
            collection.insertOne(doc);
            System
# MongoDB 教程

MongoDB 是一个基于分布式文件存储的开源 NoSQL 数据库系统,由 C++ 编写,旨在为 Web 应用提供可扩展的高性能数据存储解决方案。本教程将帮助你掌握 MongoDB 的基础知识和实用技能。

## MongoDB 简介

### 什么是 MongoDB?

MongoDB 是一个面向文档的数据库,它将数据存储在类似 JSON 的文档中,而不是传统关系数据库的表格中。这种灵活的数据模型使得 MongoDB 能够存储结构复杂的数据,并且可以随着应用需求的变化而轻松调整数据模式。

### MongoDB 的主要特点

- **文档数据模型**:数据以 BSON(二进制 JSON)格式存储,支持嵌套、数组等复杂结构
- **高性能**:支持索引、分片、复制集,提供高吞吐量和低延迟
- **高可用性**:通过复制集实现数据冗余和自动故障转移
- **水平扩展**:通过分片实现数据分布式存储,支持大规模数据集
- **灵活的查询语言**:强大的查询功能,支持聚合、全文搜索等
- **无模式设计**:集合中的文档可以有不同的字段,无需预定义结构

### MongoDB 与关系型数据库的比较

| SQL 术语/概念 | MongoDB 术语/概念 |
|--------------|-----------------|
| 数据库 (Database) | 数据库 (Database) |
| 表 (Table) | 集合 (Collection) |
| 行 (Row) | 文档 (Document) |
| 列 (Column) | 字段 (Field) |
| 主键 (Primary Key) | _id 字段 |
| 索引 (Index) | 索引 (Index) |
| 表连接 (Table Joins) | $lookup, 嵌入文档 |
| 外键 (Foreign Key) | 引用 (References) |

## 安装 MongoDB

### Windows 安装

1. 访问 [MongoDB 下载页面](https://www.mongodb.com/try/download/community)
2. 选择 Windows 版本并下载 MSI 安装包
3. 运行安装程序,按照向导完成安装
4. 可选择安装 MongoDB Compass(图形化管理工具)
5. 安装完成后,MongoDB 服务会自动启动

### macOS 安装

使用 Homebrew 安装:

```bash
# 安装 Homebrew(如果尚未安装)
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# 安装 MongoDB
brew tap mongodb/brew
brew install mongodb-community

# 启动 MongoDB 服务
brew services start mongodb-community

Linux 安装 (Ubuntu) ​

bash
# 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-5.0.asc | sudo apt-key add -

# 创建源列表文件
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/5.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-5.0.list

# 更新包数据库
sudo apt-get update

# 安装 MongoDB
sudo apt-get install -y mongodb-org

# 启动 MongoDB 服务
sudo systemctl start mongod

# 设置开机自启
sudo systemctl enable mongod

Docker 安装 ​

bash
# 拉取 MongoDB 镜像
docker pull mongo:latest

# 运行 MongoDB 容器
docker run -d --name mongodb -p 27017:27017 -v mongodb_data:/data/db mongo:latest

验证安装 ​

安装完成后,可以通过以下命令验证 MongoDB 是否正常运行:

bash
# 连接到 MongoDB Shell
mongo
# 或使用新版客户端
mongosh

# 显示版本信息
db.version()

vitepress开发指南