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开发指南