MongoDB完整学习指南 - 从入门到精通
1. MongoDB简介
1.1. 什么是MongoDB?
MongoDB是一个基于分布式文件存储的NoSQL数据库,由C++编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案。
MongoDB的特点:
- 面向文档存储(Document-Oriented)
 - 模式自由(Schema-Free)
 - 支持动态查询
 - 支持完全索引
 - 支持复制和故障恢复
 - 使用高效的二进制数据存储,包括大型对象(如视频等)
 - 自动处理负载均衡
 - 支持Python、PHP、Ruby、Java、C、C#、Javascript、Perl及C++语言的驱动程序
 
1.2. MongoDB vs 关系型数据库
| 特性 | MongoDB | 关系型数据库 | 
|---|---|---|
| 数据模型 | 文档模型 | 关系模型 | 
| 表结构 | 灵活的Schema | 固定的Schema | 
| 查询语言 | MongoDB查询语法 | SQL | 
| 事务支持 | 支持ACID事务 | 强ACID事务 | 
| 扩展方式 | 水平扩展 | 主要垂直扩展 | 
| 存储格式 | BSON | 行列存储 | 
2. 核心概念
2.1. 基本术语对比
关系型数据库    MongoDB
数据库         数据库(Database)
表(Table)      集合(Collection)
行(Row)        文档(Document)
列(Column)     字段(Field)
索引           索引
表连接         嵌入文档或引用
主键           主键(MongoDB提供了默认的_id字段)
2.2. BSON数据类型
MongoDB使用BSON(Binary JSON)格式存储数据,支持以下数据类型:
// 基本数据类型示例
{
  "name": "张三",                    // String
  "age": 25,                        // Number (Int32)
  "salary": 8500.50,               // Number (Double)
  "isActive": true,                // Boolean
  "birthDate": new Date(),         // Date
  "address": {                     // Object (嵌套文档)
    "city": "北京",
    "district": "朝阳区"
  },
  "skills": ["Java", "Python", "MongoDB"],  // Array
  "avatar": null,                  // Null
  "userId": ObjectId("..."),       // ObjectId
  "metadata": {                    // Mixed
    "created": new Date(),
    "tags": ["developer", "backend"]
  }
}
2.3. 集合(Collection)
集合是MongoDB中的文档组,类似于关系型数据库中的表。集合的特点:
- 集合名不能以"system.“开头(系统集合保留前缀)
 - 集合名不能包含空字符
 - 集合名不能为空字符串(”")
 - 集合名最好有意义
 
3. 部署模式
MongoDB 支持三种主要部署架构:
- 单例(Standalone):
- 单个 MongoDB 实例,运行在一个服务器上。
 - 适用于开发、测试或低负载场景。
 - 没有冗余或故障转移,数据丢失风险较高。
 
 - 副本集(Replica Set):
- 多个 MongoDB 节点(通常 3 个或更多),包含一个主节点和多个从节点(及可选仲裁者)。
 - 提供高可用性、数据冗余和自动故障转移。
 - 适用于生产环境,需要高可用性和数据一致性。
 
 - 分片(Sharded Cluster):
- 数据分布在多个分片(shard)上,每个分片可以是一个副本集。
 - 提供水平扩展,适合处理大规模数据和高并发。
 - 需要额外的组件,如配置服务器(Config Server)和查询路由(Mongos)。
 
 
3.1. 三种架构对比对比**
| 特性 | 单例(Standalone) | 副本集(Replica Set) | 分片(Sharded Cluster) | 
|---|---|---|---|
| 高可用性 | 无(单点故障) | 支持(自动故障转移) | 支持(每个分片为副本集) | 
| 数据冗余 | 无 | 有(从节点复制) | 有(分片内副本集) | 
| 水平扩展 | 不支持 | 有限(读扩展) | 支持(数据分布) | 
| 部署复杂度 | 低 | 中等 | 高 | 
| 适用场景 | 开发、测试 | 中型生产环境 | 大规模、高并发生产环境 | 
| 资源需求 | 低 | 中等(多节点) | 高(多分片、Mongos、配置服务器) | 
3.2. 副本集架构组成
MongoDB副本集通常由以下节点组成:
- 主节点(Primary):负责处理所有写操作和读操作(默认情况下)。只能有一个主节点,它是数据的权威来源。
 - 从节点(Secondary):从主节点复制数据,可以处理读操作(如果配置了读偏好)。通常有1-2个从节点。
 - 仲裁节点(Arbiter):不存储数据,仅参与选举过程。当副本集成员数量为偶数时使用,确保选举能够达到多数票。
 
工作原理:
数据复制过程:
- 主节点将所有写操作记录到操作日志(oplog)中
 - 从节点异步地从主节点的oplog中拉取操作记录
 - 从节点按顺序重放这些操作,保持与主节点的数据同步
 
选举机制:当主节点不可用时,剩余节点会自动进行选举:
- 每个节点都有优先级设置(0-1000)
 - 具有最高优先级且数据最新的节点成为新主节点
 - 选举需要获得多数节点(N/2+1)的支持
 
心跳检测:
- 节点间每2秒发送一次心跳
 - 如果主节点10秒内未响应,触发选举过程
 - 确保集群能够快速检测和响应故障
 
一致性保证
- 写关注(Write Concern):可以配置写操作需要多少个节点确认才算成功,平衡性能和一致性需求。
 - 读偏好(Read Preference):可以配置读操作的目标节点,如只读主节点、优先读从节点等。
 - 这种架构设计使MongoDB能够在节点故障时自动恢复,同时提供数据冗余和读取扩展能力,是生产环境中实现高可用性的重要机制。
 
测试选举:
# 方法一:使用rs.stepDown()命令(推荐)
# 连接到当前的主节点(mongodb-0),执行:
# 让主节点主动退位,60秒内不参与选举
rs.stepDown(60)
# 这个命令会:
1. 让mongodb-1. 0主动放弃主节点身份
2. 在60秒内不参与新的选举
3. 触发其他节点进行选举
# 方法二:临时降低优先级
# 获取当前配置
cfg = rs.conf()
# 将mongodb-0的优先级设为0(不能成为主节点)
cfg.members[0].priority = 0
# 重新配置副本集
rs.reconfig(cfg)
# 观察选举过程
# 检查副本集状态
rs.status()
# 查看选举统计
rs.printReplicationInfo()
# 先切换到admin数据库
use admin
# 实时监控状态变化
while(true) {
  try {
    var status = rs.isMaster()
    var replStatus = rs.status()
    var myState = replStatus.myState
    print(new Date() + ": isMaster=" + status.ismaster + 
          " | primary=" + (status.primary || "NONE") + 
          " | myState=" + myState)
  } catch(e) {
    print(new Date() + ": Error - " + e.message)
  }
  sleep(1000)
}
# 立即检查状态
rs.status()
# 再次检查谁是新主节点
rs.isMaster()
3.3. 分片工作原理
MongoDB分片(Sharding)是MongoDB的水平扩展解决方案,通过将数据分布在多个服务器上来处理大量数据和高并发访问。
分片架构组件
分片(Shards):
- 每个分片是一个独立的副本集或单个mongod实例
 - 存储数据集的一个子集
 - 负责处理分配给它的数据范围的读写操作
 
配置服务器(Config Servers):
- 存储分片集群的元数据和配置信息
 - 记录每个分片包含哪些数据块(chunks)
 - 通常部署为3个节点的副本集,确保高可用性
 
查询路由器(mongos):
- 作为客户端和分片集群之间的接口
 - 根据分片键将查询路由到正确的分片
 - 聚合来自多个分片的查询结果
 - 可以部署多个mongos实例提供负载均衡
 
数据分布机制
分片键(Shard Key):
- 决定文档如何在分片间分布的字段
 - 必须在所有文档中存在且不能为数组
 - 选择合适的分片键对性能至关重要
 
数据块(Chunks):
- MongoDB将数据划分为连续的数据块
 - 默认每个块最大64MB
 - 基于分片键的范围进行划分
 - 例如:用户ID 1-1000在分片A,1001-2000在分片B
 
分片策略:
- 范围分片:根据分片键值的范围分布数据,适合范围查询
 - 哈希分片:对分片键进行哈希,数据分布更均匀,但范围查询效率较低
 
工作流程
写操作:
- 客户端连接到mongos
 - mongos根据文档的分片键确定目标分片
 - 将写请求路由到相应的分片
 - 分片执行写操作并返回结果
 
读操作:
- mongos分析查询条件
 - 如果查询包含分片键,直接路由到目标分片
 - 如果不包含分片键,可能需要向所有分片发送查询(散播查询)
 - mongos聚合各分片的结果并返回给客户端
 
负载均衡
自动均衡器(Balancer):
- MongoDB内置的后台进程
 - 监控各分片间的数据分布
 - 当分片间数据量差异超过阈值时,自动迁移chunks
 - 确保数据在集群中均匀分布
 
块迁移过程:
- 源分片将chunk数据复制到目标分片
 - 目标分片确认接收完成
 - 更新配置服务器中的元数据
 - 删除源分片上的原始数据
 
分片的优势与注意事项
优势:
- 水平扩展存储和计算能力
 - 提高并发处理能力
 - 支持超大数据集
 
注意事项:
- 分片键选择很重要,一旦设定难以更改
 - 跨分片的事务和连接查询性能较差
 - 增加了架构复杂性和运维难度
 - 某些查询可能需要访问所有分片,影响性能
 
4. 三、MongoDB 架构
4.1. 单实例架构
仅一个 mongod 进程,适合开发或轻量业务场景。无高可用、扩展能力。
Client --> mongod
4.2. 副本集架构(Replica Set)
MongoDB 的高可用基本单元。
        +-------------------+
        |    Client         |
        +-------------------+
             |  |
             |  |
       +-----+  +------+
       |              |
   Primary       Secondary(s)
       |              |
   Oplog同步         读操作(可选)
最少三个节点,包含一个 Primary 和多个 Secondary,通过 Oplog 实现数据同步和 failover。
4.3. 分片集群架构(Sharded Cluster)
用于水平扩展大规模数据。
            +-------------+
            |   Client    |
            +-------------+
                   |
                 mongos(路由)
                   |
       +-----------+-----------+
       |                       |
Config Server           Config Server
       |                       |
  +---------+           +----------+
  | Shard 1 |           | Shard 2  |
  |(Replica)|           |(Replica) |
  +---------+           +----------+
- mongos:路由进程,请求分发器
 - Config Server:存储分片元数据
 - Shard:每个分片都是副本集
 
5. 安装与配置
5.1. Windows安装
# 1. 下载MongoDB Community Server
# 访问:https://www.mongodb.com/try/download/community
# 2. 安装后设置环境变量
# 将 C:\Program Files\MongoDB\Server\7.0\bin 添加到PATH
# 3. 创建数据目录
mkdir C:\data\db
# 4. 启动MongoDB服务
mongod --dbpath C:\data\db
5.2. Linux安装(Ubuntu)
# 1. 导入公钥
wget -qO - https://www.mongodb.org/static/pgp/server-7.0.asc | sudo apt-key add -
# 2. 创建源列表文件
echo "deb [ arch=amd64,arm64 ] https://repo.mongodb.org/apt/ubuntu focal/mongodb-org/7.0 multiverse" | sudo tee /etc/apt/sources.list.d/mongodb-org-7.0.list
# 3. 更新包数据库
sudo apt-get update
# 4. 安装MongoDB
sudo apt-get install -y mongodb-org
# 5. 启动MongoDB服务
sudo systemctl start mongod
sudo systemctl enable mongod
5.3. Docker安装
# 拉取MongoDB镜像
docker pull mongo:latest
# 运行MongoDB容器
docker run --name mongodb -p 27017:27017 -d mongo:latest
# 连接到MongoDB
docker exec -it mongodb mongosh
5.4. Helm 安装
# architecture 可选择:standalone, replicaset
    # standalone:单例模式
    # replicaset:副本集模式
# arbiter.enabled:是否启用仲裁节点、当节点数为偶数时,需要启用仲裁节点,仲裁节点不存储数据,只参与选举
# replicaCount:副本集数量
# auth.enabled:是否启用认证
# auth.rootPassword:root密码
# backup.enabled:是否启用备份
# metrics.enabled:是否启用监控
# serviceMonitor.enabled:是否启用ServiceMonitor
# 单例模式
helm upgrade --install mongodb oci://registry-1.docker.io/bitnamicharts/mongodb \
  -n mongodb \
  --create-namespace \
  --set global.storageClass="nfs" \
  --set architecture="standalone" \
  --set auth.enabled=true \
  --set auth.rootPassword="your-password" \
  --set replicaCount=1 \
  --set backup.enabled=true \
  --set metrics.enabled=true \
  --set serviceMonitor.enabled=false
# 副本集模式
helm upgrade --install mongodb oci://registry-1.docker.io/bitnamicharts/mongodb \
  -n mongodb \
  --create-namespace \
  --set global.storageClass="nfs" \
  --set architecture="replicaset" \
  --set auth.enabled=true \
  --set auth.rootPassword="your-password" \
  --set replicaCount=2 \
  --set arbiter.enabled=true \
  --set backup.enabled=true \
  --set metrics.enabled=true \
  --set serviceMonitor.enabled=false
# 高性能部署
# MongoDB 的 wiredTigerCacheSizeGB 参数用于配置 WiredTiger 存储引擎的内存缓存大小,直接影响数据库性能
# 专用 MongoDB 服务器:分配 60%-80% 物理内存
# 云服务器:分配 50%-60% 物理内存
helm upgrade --install mongodb oci://registry-1.docker.io/bitnamicharts/mongodb \
  -n mongodb \
  --create-namespace \
  --set global.storageClass="nfs" \
  --set architecture="replicaset" \
  --set auth.enabled=true \
  --set auth.rootPassword="your-password" \
  --set replicaCount=2 \
  --set arbiter.enabled=true \
  --set backup.enabled=true \
  --set metrics.enabled=true \
  --set serviceMonitor.enabled=false \
  --set resources.requests.cpu=4 \  
  --set resources.requests.memory="16Gi" \
  --set resources.limits.cpu=4 \
  --set resources.limits.memory="16Gi" \
  --set-string extraFlags[0]="--wiredTigerCacheSizeGB=8"
6. 基础操作
6.1. 连接数据库
// 使用mongosh连接
mongosh "mongodb://localhost:27017"
// 或指定数据库
mongosh "mongodb://localhost:27017/myapp"
6.2. 数据库操作
// 查看所有数据库
show dbs
// 创建/切换数据库
use myapp
// 查看当前数据库
db
// 删除数据库
db.dropDatabase()
// 查看数据库状态
db.stats()
6.3. 集合操作
// 创建集合
db.createCollection("users")
// 查看所有集合
show collections
// 删除集合
db.users.drop()
// 重命名集合
db.users.renameCollection("members")
6.4. 文档的CRUD操作
6.4.1. 插入文档(Create)
// 插入单个文档
db.users.insertOne({
  name: "张三",
  age: 25,
  email: "zhangsan@example.com",
  skills: ["JavaScript", "Python"],
  address: {
    city: "北京",
    district: "朝阳区"
  },
  createdAt: new Date()
})
// 插入多个文档
db.users.insertMany([
  {
    name: "李四",
    age: 28,
    email: "lisi@example.com",
    skills: ["Java", "Spring"],
    department: "后端开发"
  },
  {
    name: "王五",
    age: 30,
    email: "wangwu@example.com",
    skills: ["React", "Vue"],
    department: "前端开发"
  }
])
// 批量插入的返回结果
{
  "acknowledged": true,
  "insertedIds": [
    ObjectId("..."),
    ObjectId("...")
  ]
}
6.4.2. 查询文档(Read)
// 查询所有文档
db.users.find()
// 格式化输出
db.users.find().pretty()
// 查询指定条件
db.users.findOne({name: "张三"})
// 条件查询
db.users.find({age: {$gte: 25}})  // 年龄大于等于25
// 多条件查询
db.users.find({
  age: {$gte: 25, $lt: 30},
  department: "后端开发"
})
// 正则表达式查询
db.users.find({name: /^张/})  // 姓张的用户
// 数组字段查询
db.users.find({skills: "JavaScript"})  // 技能包含JavaScript
// 嵌套文档查询
db.users.find({"address.city": "北京"})
6.4.3. 更新文档(Update)
// 更新单个文档
db.users.updateOne(
  {name: "张三"},  // 查询条件
  {
    $set: {
      age: 26,
      email: "zhangsan_new@example.com"
    },
    $push: {
      skills: "MongoDB"
    }
  }
)
// 更新多个文档
db.users.updateMany(
  {department: "前端开发"},
  {
    $set: {
      level: "高级"
    }
  }
)
// 替换文档
db.users.replaceOne(
  {name: "李四"},
  {
    name: "李四",
    age: 29,
    email: "lisi_updated@example.com",
    skills: ["Java", "Spring", "MongoDB"],
    department: "全栈开发",
    updatedAt: new Date()
  }
)
// 更新操作符
db.users.updateOne(
  {name: "王五"},
  {
    $inc: {age: 1},           // 递增
    $unset: {department: ""},  // 删除字段
    $addToSet: {skills: "Node.js"},  // 向数组添加唯一值
    $currentDate: {lastModified: true}  // 设置当前日期
  }
)
6.4.4. 删除文档(Delete)
// 删除单个文档
db.users.deleteOne({name: "张三"})
// 删除多个文档
db.users.deleteMany({age: {$lt: 25}})
// 删除所有文档
db.users.deleteMany({})
7. 高级查询
7.1. 比较操作符
// 等于
db.products.find({price: 100})
// 不等于
db.products.find({price: {$ne: 100}})
// 大于
db.products.find({price: {$gt: 100}})
// 大于等于
db.products.find({price: {$gte: 100}})
// 小于
db.products.find({price: {$lt: 200}})
// 小于等于
db.products.find({price: {$lte: 200}})
// 在指定值内
db.products.find({category: {$in: ["电子", "图书"]}})
// 不在指定值内
db.products.find({category: {$nin: ["电子", "图书"]}})
7.2. 逻辑操作符
// AND 查询(默认)
db.products.find({
  price: {$gte: 100, $lte: 500},
  category: "电子"
})
// 显式 AND
db.products.find({
  $and: [
    {price: {$gte: 100}},
    {category: "电子"}
  ]
})
// OR 查询
db.products.find({
  $or: [
    {price: {$lt: 50}},
    {category: "图书"}
  ]
})
// NOT 查询
db.products.find({
  price: {$not: {$gt: 100}}
})
// NOR 查询(都不满足)
db.products.find({
  $nor: [
    {price: {$lt: 50}},
    {category: "电子"}
  ]
})
7.3. 数组查询
// 准备数据
db.students.insertMany([
  {
    name: "张三",
    grades: [85, 90, 78],
    subjects: ["数学", "物理", "化学"]
  },
  {
    name: "李四",
    grades: [92, 88, 95],
    subjects: ["数学", "英语", "历史"]
  }
])
// 查询数组包含特定值
db.students.find({subjects: "数学"})
// 查询数组包含所有指定值
db.students.find({subjects: {$all: ["数学", "物理"]}})
// 查询数组大小
db.students.find({grades: {$size: 3}})
// 查询数组元素范围
db.students.find({grades: {$elemMatch: {$gte: 85, $lte: 95}}})
// 数组索引查询
db.students.find({"grades.0": {$gte: 90}})  // 第一个成绩大于等于90
7.4. 投影(Projection)
// 只返回指定字段
db.users.find({}, {name: 1, age: 1})
// 排除指定字段
db.users.find({}, {_id: 0, password: 0})
// 数组切片
db.students.find({}, {
  name: 1,
  grades: {$slice: 2}  // 只返回前2个成绩
})
// 数组元素匹配位置
db.students.find(
  {grades: {$gte: 90}},
  {name: 1, "grades.$": 1}  // 只返回匹配的数组元素
)
7.5. 排序与分页
// 排序
db.products.find().sort({price: 1})    // 升序
db.products.find().sort({price: -1})   // 降序
// 多字段排序
db.products.find().sort({category: 1, price: -1})
// 分页
db.products.find()
  .sort({_id: 1})
  .limit(10)     // 限制返回10条
  .skip(20)      // 跳过前20条
// 计算总数
db.products.countDocuments({price: {$gte: 100}})
8. 索引优化
8.1. 索引基础
// 查看集合的索引
db.users.getIndexes()
// 创建单字段索引
db.users.createIndex({name: 1})    // 升序索引
db.users.createIndex({age: -1})    // 降序索引
// 创建复合索引
db.users.createIndex({age: 1, name: 1})
// 创建唯一索引
db.users.createIndex({email: 1}, {unique: true})
// 创建稀疏索引(跳过没有该字段的文档)
db.users.createIndex({phone: 1}, {sparse: true})
// 创建TTL索引(生存时间索引)
db.sessions.createIndex(
  {createdAt: 1},
  {expireAfterSeconds: 3600}  // 1小时后过期
)
8.2. 索引类型详解
// 1. 文本索引
db.articles.createIndex({
  title: "text",
  content: "text"
})
// 文本搜索
db.articles.find({$text: {$search: "MongoDB 教程"}})
// 2. 地理空间索引
db.locations.createIndex({location: "2dsphere"})
// 地理查询
db.locations.find({
  location: {
    $near: {
      $geometry: {type: "Point", coordinates: [116.4074, 39.9042]},
      $maxDistance: 1000  // 1000米内
    }
  }
})
// 3. 哈希索引(分片键常用)
db.users.createIndex({userId: "hashed"})
8.3. 索引性能分析
// 查看查询执行计划
db.users.find({age: 25}).explain("executionStats")
// 强制使用指定索引
db.users.find({age: 25}).hint({age: 1})
// 查看索引使用统计
db.users.aggregate([{$indexStats: {}}])
// 删除索引
db.users.dropIndex({age: 1})
// 删除所有索引(除了_id)
db.users.dropIndexes()
8.4. 索引优化建议
// 复合索引的字段顺序很重要
// 好的顺序:相等查询 -> 范围查询 -> 排序
db.orders.createIndex({
  status: 1,        // 相等查询
  createdAt: 1,     // 范围查询
  amount: -1        // 排序
})
// 查询示例
db.orders.find({
  status: "completed",
  createdAt: {$gte: new Date("2024-01-01")}
}).sort({amount: -1})
9. 聚合框架
9.1. 聚合管道基础
// 基本聚合管道结构
db.orders.aggregate([
  {$match: {status: "completed"}},    // 筛选阶段
  {$group: {                          // 分组阶段
    _id: "$customerId",
    totalAmount: {$sum: "$amount"},
    orderCount: {$sum: 1}
  }},
  {$sort: {totalAmount: -1}},         // 排序阶段
  {$limit: 10}                        // 限制阶段
])
9.2. 常用聚合操作
// 准备测试数据
db.sales.insertMany([
  {
    _id: 1,
    product: "iPhone",
    category: "electronics",
    price: 999,
    quantity: 2,
    date: new Date("2024-01-15"),
    salesperson: "张三"
  },
  {
    _id: 2,
    product: "MacBook",
    category: "electronics", 
    price: 1999,
    quantity: 1,
    date: new Date("2024-01-20"),
    salesperson: "李四"
  },
  {
    _id: 3,
    product: "Java编程思想",
    category: "books",
    price: 89,
    quantity: 5,
    date: new Date("2024-02-01"),
    salesperson: "张三"
  }
])
// $match - 筛选文档
db.sales.aggregate([
  {$match: {category: "electronics"}}
])
// $group - 分组聚合
db.sales.aggregate([
  {$group: {
    _id: "$category",
    totalRevenue: {$sum: {$multiply: ["$price", "$quantity"]}},
    avgPrice: {$avg: "$price"},
    maxPrice: {$max: "$price"},
    minPrice: {$min: "$price"},
    count: {$sum: 1}
  }}
])
// $project - 字段投影和计算
db.sales.aggregate([
  {$project: {
    product: 1,
    price: 1,
    quantity: 1,
    revenue: {$multiply: ["$price", "$quantity"]},
    discountPrice: {$multiply: ["$price", 0.9]},
    year: {$year: "$date"},
    month: {$month: "$date"}
  }}
])
// $sort - 排序
db.sales.aggregate([
  {$project: {
    product: 1,
    revenue: {$multiply: ["$price", "$quantity"]}
  }},
  {$sort: {revenue: -1}}
])
// $limit 和 $skip - 分页
db.sales.aggregate([
  {$sort: {date: -1}},
  {$skip: 1},
  {$limit: 2}
])
// $unwind - 展开数组
db.articles.insertOne({
  title: "MongoDB教程",
  tags: ["数据库", "NoSQL", "MongoDB"],
  author: "技术专家"
})
db.articles.aggregate([
  {$unwind: "$tags"},
  {$group: {
    _id: "$tags",
    count: {$sum: 1}
  }}
])
9.3. 高级聚合操作
// $lookup - 关联查询(类似SQL的JOIN)
// 假设有用户和订单两个集合
db.users.insertMany([
  {_id: 1, name: "张三", email: "zhang@example.com"},
  {_id: 2, name: "李四", email: "li@example.com"}
])
db.orders.insertMany([
  {_id: 1, userId: 1, product: "iPhone", amount: 999},
  {_id: 2, userId: 1, product: "iPad", amount: 699},
  {_id: 3, userId: 2, product: "MacBook", amount: 1999}
])
db.users.aggregate([
  {$lookup: {
    from: "orders",           // 关联的集合
    localField: "_id",        // 本集合的字段
    foreignField: "userId",   // 关联集合的字段
    as: "userOrders"          // 结果字段名
  }},
  {$project: {
    name: 1,
    email: 1,
    totalOrders: {$size: "$userOrders"},
    totalSpent: {$sum: "$userOrders.amount"}
  }}
])
// $facet - 多维度聚合
db.sales.aggregate([
  {$facet: {
    "byCategory": [
      {$group: {
        _id: "$category",
        total: {$sum: {$multiply: ["$price", "$quantity"]}}
      }}
    ],
    "bySalesperson": [
      {$group: {
        _id: "$salesperson", 
        total: {$sum: {$multiply: ["$price", "$quantity"]}}
      }}
    ],
    "priceRange": [
      {$bucket: {
        groupBy: "$price",
        boundaries: [0, 100, 500, 1000, 2000],
        default: "other",
        output: {count: {$sum: 1}}
      }}
    ]
  }}
])
// $addFields - 添加计算字段
db.sales.aggregate([
  {$addFields: {
    revenue: {$multiply: ["$price", "$quantity"]},
    season: {
      $switch: {
        branches: [
          {case: {$in: [{$month: "$date"}, [3, 4, 5]]}, then: "春季"},
          {case: {$in: [{$month: "$date"}, [6, 7, 8]]}, then: "夏季"},
          {case: {$in: [{$month: "$date"}, [9, 10, 11]]}, then: "秋季"}
        ],
        default: "冬季"
      }
    }
  }}
])
9.4. 聚合表达式
// 条件表达式
db.students.aggregate([
  {$project: {
    name: 1,
    score: 1,
    grade: {
      $switch: {
        branches: [
          {case: {$gte: ["$score", 90]}, then: "A"},
          {case: {$gte: ["$score", 80]}, then: "B"},
          {case: {$gte: ["$score", 70]}, then: "C"}
        ],
        default: "D"
      }
    },
    pass: {$cond: {if: {$gte: ["$score", 60]}, then: "通过", else: "不通过"}}
  }}
])
// 数组表达式
db.surveys.aggregate([
  {$project: {
    answers: 1,
    answerCount: {$size: "$answers"},
    firstAnswer: {$arrayElemAt: ["$answers", 0]},
    hasAnswers: {$gt: [{$size: "$answers"}, 0]},
    avgRating: {$avg: "$answers.rating"}
  }}
])
// 字符串表达式  
db.users.aggregate([
  {$project: {
    fullName: {$concat: ["$firstName", " ", "$lastName"]},
    nameLength: {$strLenCP: "$name"},
    upperName: {$toUpper: "$name"},
    emailDomain: {
      $arrayElemAt: [
        {$split: ["$email", "@"]}, 1
      ]
    }
  }}
])
10. 复制集与分片
10.1. 复制集(Replica Set)
// 复制集配置示例
rs.initiate({
  _id: "myReplicaSet",
  members: [
    {_id: 0, host: "mongodb1.example.com:27017"},
    {_id: 1, host: "mongodb2.example.com:27017"},  
    {_id: 2, host: "mongodb3.example.com:27017"}
  ]
})
// 查看复制集状态
rs.status()
// 添加成员
rs.add("mongodb4.example.com:27017")
// 移除成员
rs.remove("mongodb4.example.com:27017")
// 设置读偏好
db.users.find().readPref("secondary")
// 连接复制集
mongosh "mongodb://mongodb1.example.com:27017,mongodb2.example.com:27017,mongodb3.example.com:27017/myapp?replicaSet=myReplicaSet"
10.2. 分片(Sharding)
// 启动配置服务器
mongod --configsvr --replSet configReplSet --port 27019 --dbpath /data/configdb
// 启动分片服务器
mongod --shardsvr --replSet shard1 --port 27018 --dbpath /data/shard1
// 启动mongos路由器
mongos --configdb configReplSet/mongodb1:27019,mongodb2:27019,mongodb3:27019 --port 27017
// 连接到mongos并添加分片
sh.addShard("shard1/mongodb1:27018,mongodb2:27018,mongodb3:27018")
// 启用数据库分片
sh.enableSharding("myapp")
// 创建分片键
sh.shardCollection("myapp.users", {"userId": "hashed"})
// 查看分片状态
sh.status()
11. 性能优化
11.1. 查询优化
// 1. 使用索引
db.users.createIndex({age: 1, name: 1})
// 2. 限制返回字段
db.users.find({age: {$gte: 25}}, {name: 1, email: 1})
// 3. 使用limit限制结果
db.users.find({status: "active"}).limit(100)
// 4. 避免全表扫描
db.users.find({name: /^张/})  // 好:前缀匹配
db.users.find({name: /张/})   // 差:包含匹配
// 5. 使用聚合管道优化
// 差的做法:先查询再在应用层处理
const users = db.users.find({age: {$gte: 25}}).toArray()
// 在JavaScript中处理统计
// 好的做法:使用聚合管道
db.users.aggregate([
  {$match: {age: {$gte: 25}}},
  {$group: {
    _id: "$department",
    count: {$sum: 1},
    avgAge: {$avg: "$age"}
  }}
])
11.2. 写入优化
// 1. 批量写入
const bulk = db.users.initializeOrderedBulkOp()
for(let i = 0; i < 1000; i++) {
  bulk.insert({
    name: `User${i}`,
    age: Math.floor(Math.random() * 50) + 20,
    createdAt: new Date()
  })
}
bulk.execute()
// 2. 使用upsert减少查询
db.users.updateOne(
  {email: "user@example.com"},
  {
    $set: {
      name: "Updated Name",
      lastLogin: new Date()
    }
  },
  {upsert: true}  // 如果不存在则插入
)
// 3. 写入关注点设置
db.users.insertOne(
  {name: "张三", age: 25},
  {writeConcern: {w: "majority", j: true, wtimeout: 5000}}
)
11.3. 监控与诊断
// 1. 查看当前操作
db.currentOp()
// 2. 终止长时间运行的操作
db.killOp(opId)
// 3. 数据库统计信息
db.stats()
// 4. 集合统计信息
db.users.stats()
// 5. 分析慢查询
db.setProfilingLevel(2, {slowms: 100})  // 记录超过100ms的操作
db.system.profile.find().limit(5).sort({ts: -1})
// 6. 索引使用统计
db.users.aggregate([{$indexStats: {}}])
12. 实战项目
12.1. 博客系统设计
// 1. 用户集合设计
db.users.insertOne({
  _id: ObjectId(),
  username: "techblogger",
  email: "blogger@example.com",
  password: "hashed_password",
  profile: {
    firstName: "Tech",
    lastName: "Blogger",
    avatar: "https://example.com/avatar.jpg",
    bio: "热爱技术分享的程序员"
  },
  social: {
    github: "techblogger",
    twitter: "@techblogger",
    website: "https://techblog.example.com"
  },
  settings: {
    emailNotifications: true,
    privacy: "public"
  },
  stats: {
    postsCount: 0,
    followersCount: 0,
    followingCount: 0
  },
  createdAt: new Date(),
  updatedAt: new Date()
})
// 2. 文章集合设计
db.posts.insertOne({
  _id: ObjectId(),
  title: "MongoDB聚合框架深入解析",
  slug: "mongodb-aggregation-framework-deep-dive",
  content: "文章内容...",
  excerpt: "本文深入探讨MongoDB聚合框架的高级用法...",
  author: {
    userId: ObjectId("用户ID"),
    username: "techblogger",
    avatar: "https://example.com/avatar.jpg"
  },
  tags: ["MongoDB", "数据库", "聚合", "NoSQL"],
  categories: ["数据库", "后端开发"],
  status: "published", // draft, published, archived
  publishedAt: new Date(),
  stats: {
    views: 1250,
    likes: 89,
    comments: 23,
    shares: 15
  },
  seo: {
    metaTitle: "MongoDB聚合框架完整指南",
    metaDescription: "学习MongoDB聚合框架...",
    keywords: ["MongoDB", "聚合", "数据库"]
  },
  createdAt: new Date(),
  updatedAt: new Date()
})
// 3. 评论集合设计
db.comments.insertOne({
  _id: ObjectId(),
  postId: ObjectId("文章ID"),
  author: {
    userId: ObjectId("用户ID"),
    username: "reader001",
    avatar: "https://example.com/user_avatar.jpg"
  },
  content: "这篇文章写得很详细,对我很有帮助!",
  parentId: null, // 父评论ID,用于嵌套回复
  level: 0, // 嵌套层级
  likes: 5,
  dislikes: 0,
  status: "approved", // pending, approved, spam, deleted
  createdAt: new Date(),
  updatedAt: new Date()
})
// 4. 标签集合设计
db.tags.insertOne({
  _id: ObjectId(),
  name: "MongoDB",
  slug: "mongodb",
  description: "MongoDB数据库相关文章",
  color: "#13aa52",
  postsCount: 45,
  followersCount: 128,
  createdAt: new Date()
})
12.2. 博客系统核心功能实现
// 1. 发布文章
function publishPost(postData) {
  const session = db.getMongo().startSession()
  
  try {
    session.startTransaction()
    
    // 插入文章
    const postResult = db.posts.insertOne({
      ...postData,
      publishedAt: new Date(),
      status: "published",
      stats: {
        views: 0,
        likes: 0,
        comments: 0,
        shares: 0
      }
    }, {session})
    
    // 更新用户文章数
    db.users.updateOne(
      {_id: postData.author.userId},
      {$inc: {"stats.postsCount": 1}},
      {session}
    )
    
    // 更新标签文章数
    if (postData.tags && postData.tags.length > 0) {
      db.tags.updateMany(
        {name: {$in: postData.tags}},
        {$inc: {postsCount: 1}},
        {session}
      )
    }
    
    session.commitTransaction()
    return postResult.insertedId
    
  } catch (error) {
    session.abortTransaction()
    throw error
  } finally {
    session.endSession()
  }
}
// 2. 文章列表查询(支持分页、排序、筛选)
function getPostsList(options = {}) {
  const {
    page = 1,
    limit = 10,
    author,
    tags,
    category,
    search,
    sortBy = "publishedAt",
    sortOrder = -1
  } = options
  
  let matchStage = {status: "published"}
  
  // 添加筛选条件
  if (author) matchStage["author.username"] = author
  if (tags) matchStage.tags = {$in: Array.isArray(tags) ? tags : [tags]}
  if (category) matchStage.categories = category
  if (search) {
    matchStage.$text = {$search: search}
  }
  
  return db.posts.aggregate([
    {$match: matchStage},
    {$sort: {[sortBy]: sortOrder}},
    {$skip: (page - 1) * limit},
    {$limit: limit},
    {$project: {
      title: 1,
      slug: 1,
      excerpt: 1,
      author: 1,
      tags: 1,
      categories: 1,
      publishedAt: 1,
      stats: 1,
      readTime: {
        $round: [{$divide: [{$strLenCP: "$content"}, 200]}] // 估算阅读时间
      }
    }}
  ])
}
// 3. 热门文章统计
function getPopularPosts(timeRange = 7) {
  const startDate = new Date()
  startDate.setDate(startDate.getDate() - timeRange)
  
  return db.posts.aggregate([
    {$match: {
      status: "published",
      publishedAt: {$gte: startDate}
    }},
    {$addFields: {
      popularityScore: {
        $add: [
          {$multiply: ["$stats.views", 1]},
          {$multiply: ["$stats.likes", 5]},
          {$multiply: ["$stats.comments", 10]},
          {$multiply: ["$stats.shares", 15]}
        ]
      }
    }},
    {$sort: {popularityScore: -1}},
    {$limit: 10},
    {$project: {
      title: 1,
      slug: 1,
      author: 1,
      publishedAt: 1,
      stats: 1,
      popularityScore: 1
    }}
  ])
}
// 4. 用户关注系统
db.follows.insertOne({
  _id: ObjectId(),
  follower: ObjectId("关注者ID"),
  following: ObjectId("被关注者ID"),
  createdAt: new Date()
})
// 获取用户关注的人的文章
function getFollowingPosts(userId, page = 1, limit = 10) {
  return db.follows.aggregate([
    {$match: {follower: ObjectId(userId)}},
    {$lookup: {
      from: "posts",
      localField: "following",
      foreignField: "author.userId",
      as: "posts"
    }},
    {$unwind: "$posts"},
    {$match: {"posts.status": "published"}},
    {$sort: {"posts.publishedAt": -1}},
    {$skip: (page - 1) * limit},
    {$limit: limit},
    {$replaceRoot: {newRoot: "$posts"}}
  ])
}
// 5. 搜索功能
db.posts.createIndex({
  title: "text",
  content: "text",
  tags: "text"
}, {
  weights: {
    title: 10,
    tags: 5,
    content: 1
  },
  name: "posts_text_index"
})
function searchPosts(query, page = 1, limit = 10) {
  return db.posts.aggregate([
    {$match: {
      $text: {$search: query},
      status: "published"
    }},
    {$addFields: {
      score: {$meta: "textScore"}
    }},
    {$sort: {score: {$meta: "textScore"}}},
    {$skip: (page - 1) * limit},
    {$limit: limit}
  ])
}
12.3. 电商系统设计
// 1. 商品集合设计
db.products.insertOne({
  _id: ObjectId(),
  name: "iPhone 15 Pro",
  slug: "iphone-15-pro",
  description: "最新款iPhone,搭载A17 Pro芯片",
  category: {
    main: "电子产品",
    sub: "智能手机",
    path: ["电子产品", "智能手机", "苹果"]
  },
  brand: "Apple",
  sku: "IPH15P-256-BLU",
  variants: [
    {
      sku: "IPH15P-128-BLU",
      attributes: {
        storage: "128GB",
        color: "深空蓝色"
      },
      price: 8999,
      stock: 50,
      images: ["url1.jpg", "url2.jpg"]
    },
    {
      sku: "IPH15P-256-BLU", 
      attributes: {
        storage: "256GB",
        color: "深空蓝色"
      },
      price: 9999,
      stock: 30,
      images: ["url3.jpg", "url4.jpg"]
    }
  ],
  specifications: {
    "屏幕尺寸": "6.1英寸",
    "处理器": "A17 Pro",
    "摄像头": "4800万像素主摄",
    "电池": "3274mAh"
  },
  tags: ["5G", "Face ID", "无线充电"],
  seo: {
    title: "iPhone 15 Pro - Apple官方授权",
    description: "购买iPhone 15 Pro...",
    keywords: ["iPhone", "苹果手机", "5G手机"]
  },
  pricing: {
    basePrice: 8999,
    salePrice: null,
    wholesalePrice: 8500,
    currency: "CNY"
  },
  inventory: {
    totalStock: 80,
    reserved: 5,
    available: 75,
    lowStockThreshold: 10
  },
  ratings: {
    average: 4.8,
    count: 1250,
    distribution: {
      "5": 980,
      "4": 200,
      "3": 50,
      "2": 15,
      "1": 5
    }
  },
  status: "active", // active, inactive, discontinued
  isDigital: false,
  shippingInfo: {
    weight: 0.187, // kg
    dimensions: {
      length: 14.76,
      width: 7.15,
      height: 0.83
    },
    freeShipping: true,
    shippingMethods: ["standard", "express", "same-day"]
  },
  createdAt: new Date(),
  updatedAt: new Date()
})
// 2. 订单集合设计
db.orders.insertOne({
  _id: ObjectId(),
  orderNumber: "ORD20240807001",
  customer: {
    userId: ObjectId("用户ID"),
    email: "customer@example.com",
    name: "张三",
    phone: "13800138000"
  },
  items: [
    {
      productId: ObjectId("商品ID"),
      sku: "IPH15P-256-BLU",
      name: "iPhone 15 Pro 256GB 深空蓝色",
      price: 9999,
      quantity: 1,
      subtotal: 9999,
      attributes: {
        storage: "256GB",
        color: "深空蓝色"
      }
    }
  ],
  pricing: {
    subtotal: 9999,
    shipping: 0,
    tax: 899.91,
    discount: 0,
    total: 10898.91,
    currency: "CNY"
  },
  shipping: {
    method: "express",
    address: {
      recipient: "张三",
      phone: "13800138000",
      province: "北京市",
      city: "北京市", 
      district: "朝阳区",
      street: "XXX街道XXX号",
      postalCode: "100000"
    },
    estimatedDelivery: new Date("2024-08-09")
  },
  payment: {
    method: "alipay",
    status: "paid",
    transactionId: "ALI20240807001",
    paidAt: new Date()
  },
  status: "processing", // pending, paid, processing, shipped, delivered, cancelled, refunded
  timeline: [
    {
      status: "pending",
      timestamp: new Date("2024-08-07T10:00:00Z"),
      note: "订单已创建"
    },
    {
      status: "paid", 
      timestamp: new Date("2024-08-07T10:05:00Z"),
      note: "支付成功"
    }
  ],
  createdAt: new Date(),
  updatedAt: new Date()
})
// 3. 购物车集合设计
db.carts.insertOne({
  _id: ObjectId(),
  userId: ObjectId("用户ID"),
  items: [
    {
      productId: ObjectId("商品ID"),
      sku: "IPH15P-256-BLU",
      quantity: 1,
      addedAt: new Date(),
      price: 9999 // 添加时的价格
    }
  ],
  totals: {
    itemCount: 1,
    subtotal: 9999
  },
  createdAt: new Date(),
  updatedAt: new Date(),
  expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7天后过期
})
// 4. 库存管理
function updateInventory(sku, quantityChange, operation = "reserve") {
  const session = db.getMongo().startSession()
  
  try {
    session.startTransaction()
    
    const product = db.products.findOne(
      {"variants.sku": sku},
      {session}
    )
    
    if (!product) {
      throw new Error("商品不存在")
    }
    
    const variant = product.variants.find(v => v.sku === sku)
    if (operation === "reserve" && variant.stock < quantityChange) {
      throw new Error("库存不足")
    }
    
    // 更新库存
    const updateDoc = {}
    updateDoc[`variants.$.stock`] = variant.stock - quantityChange
    
    db.products.updateOne(
      {"variants.sku": sku},
      {$inc: updateDoc},
      {session}
    )
    
    // 记录库存变动
    db.inventoryLogs.insertOne({
      sku: sku,
      operation: operation,
      quantity: quantityChange,
      previousStock: variant.stock,
      newStock: variant.stock - quantityChange,
      reason: "订单处理",
      timestamp: new Date()
    }, {session})
    
    session.commitTransaction()
    
  } catch (error) {
    session.abortTransaction()
    throw error
  } finally {
    session.endSession()
  }
}
// 5. 商品推荐系统
function getRecommendations(userId, limit = 10) {
  return db.orders.aggregate([
    // 找出用户购买过的商品类别
    {$match: {"customer.userId": ObjectId(userId)}},
    {$unwind: "$items"},
    {$lookup: {
      from: "products",
      localField: "items.productId",
      foreignField: "_id",
      as: "product"
    }},
    {$unwind: "$product"},
    
    // 统计用户偏好的类别
    {$group: {
      _id: "$product.category.main",
      purchaseCount: {$sum: 1}
    }},
    {$sort: {purchaseCount: -1}},
    {$limit: 3},
    
    // 查找相似类别的热门商品
    {$lookup: {
      from: "products",
      let: {category: "$_id"},
      pipeline: [
        {$match: {
          $expr: {$eq: ["$category.main", "$category"]},
          status: "active"
        }},
        {$sort: {"ratings.average": -1, "ratings.count": -1}},
        {$limit: limit}
      ],
      as: "recommendations"
    }},
    
    {$unwind: "$recommendations"},
    {$replaceRoot: {newRoot: "$recommendations"}},
    {$limit: limit}
  ])
}
13. 面试问题集锦
13.1. 基础概念题
Q1: 什么是MongoDB?它与关系型数据库有什么区别?
A: MongoDB是一个面向文档的NoSQL数据库,主要区别包括:
- 数据模型: MongoDB存储BSON文档,关系型数据库存储表格数据
 - Schema: MongoDB是schema-free,关系型数据库有固定schema
 - 扩展性: MongoDB易于水平扩展,关系型数据库主要垂直扩展
 - 查询语言: MongoDB使用自己的查询语法,关系型数据库使用SQL
 - 事务: MongoDB 4.0+支持多文档事务,关系型数据库天然支持ACID事务
 
Q2: 解释MongoDB中的BSON是什么?
A: BSON (Binary JSON) 是MongoDB使用的二进制格式:
- 扩展了JSON类型系统,增加了Date、ObjectId、Binary等类型
 - 比JSON更节省空间和更快的解析速度
 - 保持了JSON的易读性和灵活性
 - 支持嵌套文档和数组
 
Q3: 什么是ObjectId?它的结构是什么?
A: ObjectId是MongoDB中文档的默认主键:
ObjectId = 4字节时间戳 + 5字节随机值 + 3字节递增计数器
- 前4字节:Unix时间戳,确保时间排序
- 接下来5字节:机器和进程的随机值
- 最后3字节:自增计数器,确保同一秒内的唯一性
13.2. 查询与索引题
Q4: 解释MongoDB中的索引类型?
A: MongoDB支持多种索引类型:
// 1. 单字段索引
db.collection.createIndex({field: 1})
// 2. 复合索引  
db.collection.createIndex({field1: 1, field2: -1})
// 3. 多键索引(数组字段自动创建)
db.collection.createIndex({arrayField: 1})
// 4. 文本索引
db.collection.createIndex({title: "text", content: "text"})
// 5. 地理空间索引
db.collection.createIndex({location: "2dsphere"})
// 6. 哈希索引
db.collection.createIndex({field: "hashed"})
// 7. 稀疏索引
db.collection.createIndex({field: 1}, {sparse: true})
// 8. 唯一索引
db.collection.createIndex({email: 1}, {unique: true})
// 9. TTL索引
db.collection.createIndex({createdAt: 1}, {expireAfterSeconds: 3600})
Q5: 如何优化MongoDB查询性能?
A: MongoDB查询优化策略:
// 1. 使用explain()分析查询
db.collection.find({field: value}).explain("executionStats")
// 2. 创建合适的索引
db.collection.createIndex({queryField: 1})
// 3. 复合索引的字段顺序:等值查询 -> 范围查询 -> 排序
db.collection.createIndex({status: 1, createdAt: 1, priority: -1})
// 4. 使用投影限制返回字段
db.collection.find({}, {name: 1, email: 1})
// 5. 使用limit限制结果集
db.collection.find().limit(100)
// 6. 避免否定查询
db.collection.find({status: {$ne: "inactive"}}) // 避免
db.collection.find({status: "active"}) // 推荐
// 7. 使用聚合管道优化复杂查询
db.collection.aggregate([
  {$match: {status: "active"}}, // 先过滤
  {$sort: {createdAt: -1}},     // 再排序
  {$limit: 100}                 // 最后限制
])
13.3. 聚合框架题
Q6: 解释MongoDB聚合框架的工作原理?
A: MongoDB聚合框架基于管道(Pipeline)概念:
// 聚合管道示例
db.orders.aggregate([
  // 第1阶段:匹配条件
  {$match: {
    status: "completed",
    orderDate: {$gte: new Date("2024-01-01")}
  }},
  
  // 第2阶段:展开数组字段
  {$unwind: "$items"},
  
  // 第3阶段:分组聚合
  {$group: {
    _id: "$items.category",
    totalRevenue: {$sum: {$multiply: ["$items.price", "$items.quantity"]}},
    orderCount: {$sum: 1},
    avgOrderValue: {$avg: "$totalAmount"}
  }},
  
  // 第4阶段:排序
  {$sort: {totalRevenue: -1}},
  
  // 第5阶段:限制结果
  {$limit: 10}
])
Q7: $lookup操作符如何使用?什么时候使用?
A: $lookup用于实现类似SQL JOIN的关联查询:
// 基本用法
db.orders.aggregate([
  {$lookup: {
    from: "users",              // 关联集合
    localField: "userId",       // 本集合字段
    foreignField: "_id",        // 关联集合字段  
    as: "userInfo"             // 输出字段名
  }}
])
// 高级用法:带条件的lookup
db.orders.aggregate([
  {$lookup: {
    from: "products",
    let: {orderItems: "$items"},
    pipeline: [
      {$match: {
        $expr: {$in: ["$_id", "$orderItems.productId"]},
        status: "active"
      }},
      {$project: {name: 1, price: 1}}
    ],
    as: "productDetails"
  }}
])
// 使用场景:
// 1. 需要展示关联数据(如订单显示用户信息)
// 2. 数据统计分析(如用户订单汇总)
// 3. 替代应用层的多次查询
13.4. 复制集与分片题
Q8: 什么是MongoDB复制集?如何配置?
A: 复制集是MongoDB的高可用解决方案:
// 初始化复制集
rs.initiate({
  _id: "myReplicaSet",
  members: [
    {_id: 0, host: "mongo1:27017", priority: 2}, // Primary优先级高
    {_id: 1, host: "mongo2:27017", priority: 1}, // Secondary
    {_id: 2, host: "mongo3:27017", priority: 0, arbiterOnly: true} // Arbiter
  ]
})
// 特点:
// 1. 自动故障转移
// 2. 数据冗余备份  
// 3. 读写分离(可配置读偏好)
// 4. 最少需要3个节点(或2个数据节点+1个仲裁节点)
Q9: 什么时候需要使用分片?如何设计分片键?
A: 分片的使用场景和分片键设计:
// 使用分片的场景:
// 1. 数据量超过单机存储能力(TB级别)
// 2. 读写QPS超过单机处理能力
// 3. 需要水平扩展存储和计算能力
// 分片键设计原则:
// 1. 高基数(Cardinality)- 取值范围要广
// 2. 低频率(Frequency)- 避免数据倾斜
// 3. 单调变化避免(Non-monotonically changing)
// 好的分片键示例:
sh.shardCollection("app.users", {userId: "hashed"}) // 哈希分片
sh.shardCollection("app.logs", {timestamp: 1, userId: 1}) // 复合分片键
sh.shardCollection("app.products", {category: 1, productId: 1})
// 避免的分片键:
// {_id: 1} - ObjectId是单调递增的
// {timestamp: 1} - 时间戳单调递增
// {status: 1} - 基数太低
13.5. 性能优化题
Q10: 如何监控MongoDB性能?
A: MongoDB性能监控方法:
// 1. 数据库性能分析
db.enableFreeMonitoring() // 启用免费监控
// 2. 慢查询分析
db.setProfilingLevel(2, {slowms: 100}) // 记录>100ms的操作
db.system.profile.find().limit(5).sort({ts: -1}).pretty()
// 3. 实时监控
db.serverStatus() // 服务器状态
db.stats() // 数据库统计
db.collection.stats() // 集合统计
// 4. 当前操作监控
db.currentOp({
  "active": true,
  "secs_running": {"$gte": 5}
}) // 查看运行超过5秒的操作
// 5. 索引使用分析
db.collection.aggregate([{$indexStats: {}}])
// 6. 连接池监控
db.serverStatus().connections
// 7. 锁信息
db.serverStatus().globalLock
Q11: MongoDB写关注和读偏好如何配置?
A: 写关注(Write Concern)和读偏好(Read Preference)配置:
// 写关注级别
db.collection.insertOne(
  {name: "test"},
  {
    writeConcern: {
      w: "majority",        // 等待大多数节点确认
      j: true,              // 等待写入journal
      wtimeout: 5000        // 5秒超时
    }
  }
)
// 写关注选项:
// w: 1 - 只等待primary确认(默认)
// w: "majority" - 等待大多数节点确认
// w: 0 - 不等待确认(最快但不安全)
// 读偏好设置
db.collection.find().readPref("secondary") // 从secondary读取
db.collection.find().readPref("primaryPreferred") // 优先primary
// 读偏好选项:
// primary - 只从primary读(默认)
// primaryPreferred - 优先primary,不可用时从secondary
// secondary - 只从secondary读
// secondaryPreferred - 优先secondary,不可用时从primary
// nearest - 从网络延迟最小的节点读
13.6. 事务与一致性题
Q12: MongoDB支持事务吗?如何使用?
A: MongoDB 4.0+支持多文档ACID事务:
// 单文档事务(自动支持)
db.accounts.updateOne(
  {_id: "account1"},
  {$inc: {balance: -100}}
)
// 多文档事务
const session = db.getMongo().startSession()
try {
  session.startTransaction({
    readConcern: {level: "snapshot"},
    writeConcern: {w: "majority"}
  })
  
  // 转账操作
  db.accounts.updateOne(
    {_id: "account1"},
    {$inc: {balance: -100}},
    {session}
  )
  
  db.accounts.updateOne(
    {_id: "account2"}, 
    {$inc: {balance: 100}},
    {session}
  )
  
  // 记录转账日志
  db.transactions.insertOne({
    from: "account1",
    to: "account2",
    amount: 100,
    timestamp: new Date()
  }, {session})
  
  session.commitTransaction()
  
} catch (error) {
  session.abortTransaction()
  throw error
} finally {
  session.endSession()
}
// 事务使用场景:
// 1. 金融转账操作
// 2. 订单处理(扣库存、创建订单、更新用户积分)
// 3. 多集合数据一致性要求的操作
Q13: 如何处理MongoDB的数据一致性问题?
A: MongoDB数据一致性保障策略:
// 1. 文档级原子性(单文档操作天然原子)
db.users.updateOne(
  {_id: userId},
  {
    $inc: {balance: -100, orderCount: 1},
    $push: {orders: orderId},
    $set: {lastOrderDate: new Date()}
  }
)
// 2. 使用事务保证多文档一致性
function transferMoney(fromId, toId, amount) {
  const session = db.getMongo().startSession()
  
  try {
    session.startTransaction()
    
    const fromAccount = db.accounts.findOne({_id: fromId}, {session})
    if (fromAccount.balance < amount) {
      throw new Error("余额不足")
    }
    
    db.accounts.updateOne(
      {_id: fromId},
      {$inc: {balance: -amount}},
      {session}
    )
    
    db.accounts.updateOne(
      {_id: toId},
      {$inc: {balance: amount}},
      {session}
    )
    
    session.commitTransaction()
  } catch (error) {
    session.abortTransaction()
    throw error
  } finally {
    session.endSession()
  }
}
// 3. 最终一致性模式(适用于不需要强一致性的场景)
// 订单创建 -> 异步更新库存 -> 异步更新用户统计
function createOrderEventually(orderData) {
  // 1. 先创建订单
  const orderId = db.orders.insertOne(orderData).insertedId
  
  // 2. 发布事件到消息队列
  publishEvent('order.created', {
    orderId: orderId,
    items: orderData.items,
    userId: orderData.userId
  })
  
  return orderId
}
// 4. 使用唯一索引防止重复
db.orders.createIndex({orderNumber: 1}, {unique: true})
db.users.createIndex({email: 1}, {unique: true})
13.7. 实际应用题
Q14: 如何设计一个高并发的商品秒杀系统?
A: MongoDB实现秒杀系统的关键设计:
// 1. 商品库存设计(使用版本号乐观锁)
db.products.insertOne({
  _id: ObjectId("product_id"),
  name: "限量商品",
  stock: 100,
  version: 1,  // 版本号
  saleStart: new Date("2024-08-07T20:00:00Z"),
  saleEnd: new Date("2024-08-07T20:05:00Z")
})
// 2. 秒杀逻辑(原子操作减库存)
function seckill(productId, userId, quantity = 1) {
  const now = new Date()
  
  // 预检查(减少数据库压力)
  const product = db.products.findOne({
    _id: ObjectId(productId),
    stock: {$gte: quantity},
    saleStart: {$lte: now},
    saleEnd: {$gte: now}
  })
  
  if (!product) {
    return {success: false, message: "商品不存在或活动未开始"}
  }
  
  // 原子减库存
  const result = db.products.updateOne({
    _id: ObjectId(productId),
    stock: {$gte: quantity},
    version: product.version  // 乐观锁
  }, {
    $inc: {stock: -quantity, version: 1},
    $push: {
      purchaseLog: {
        userId: userId,
        quantity: quantity,
        timestamp: now
      }
    }
  })
  
  if (result.modifiedCount === 0) {
    return {success: false, message: "库存不足或商品已更新"}
  }
  
  // 创建订单(可异步处理)
  const orderId = db.orders.insertOne({
    userId: userId,
    productId: ObjectId(productId),
    quantity: quantity,
    status: "pending",
    createdAt: now
  }).insertedId
  
  return {success: true, orderId: orderId}
}
// 3. 防刷机制
db.seckillLogs.createIndex({userId: 1, productId: 1}, {unique: true}) // 防止重复购买
db.seckillLogs.createIndex({ip: 1, createdAt: 1}) // IP限制
db.seckillLogs.createIndex({createdAt: 1}, {expireAfterSeconds: 3600}) // 日志过期
// 4. 缓存预热和降级
// 使用Redis缓存商品信息和库存
// MongoDB作为最终数据持久化
Q15: 如何处理MongoDB的大数据量查询和分页?
A: 大数据量处理和分页优化:
// 1. 避免使用skip进行深度分页(性能差)
// 错误做法:
db.posts.find().skip(10000).limit(20) // 非常慢
// 2. 使用游标分页(Cursor Pagination)
function getCursorPagination(lastId = null, limit = 20) {
  let query = {}
  if (lastId) {
    query._id = {$gt: ObjectId(lastId)}
  }
  
  const results = db.posts.find(query)
    .sort({_id: 1})
    .limit(limit + 1) // 多查一个判断是否有下一页
  
  const hasNext = results.length > limit
  if (hasNext) {
    results.pop() // 移除多余的一个
  }
  
  return {
    data: results,
    hasNext: hasNext,
    nextCursor: results.length > 0 ? results[results.length - 1]._id : null
  }
}
// 3. 时间范围分页
function getTimeBasedPagination(lastDate = null, limit = 20) {
  let query = {}
  if (lastDate) {
    query.createdAt = {$lt: new Date(lastDate)}
  }
  
  return db.posts.find(query)
    .sort({createdAt: -1})
    .limit(limit)
}
// 4. 聚合分页(复杂查询场景)
function getAggregationPagination(page = 1, limit = 20, filters = {}) {
  const skip = (page - 1) * limit
  
  const pipeline = [
    {$match: filters},
    {$sort: {createdAt: -1}},
    {$facet: {
      data: [
        {$skip: skip},
        {$limit: limit}
      ],
      totalCount: [
        {$count: "count"}
      ]
    }}
  ]
  
  const result = db.posts.aggregate(pipeline).next()
  const total = result.totalCount[0]?.count || 0
  
  return {
    data: result.data,
    pagination: {
      page: page,
      limit: limit,
      total: total,
      pages: Math.ceil(total / limit),
      hasNext: skip + limit < total,
      hasPrev: page > 1
    }
  }
}
// 5. 大数据量统计优化
// 使用聚合管道 + 采样
db.hugecollection.aggregate([
  {$sample: {size: 10000}}, // 随机采样
  {$group: {
    _id: "$category",
    avgPrice: {$avg: "$price"},
    count: {$sum: 1}
  }}
])
// 6. 分批处理大数据集
function processBigData(batchSize = 1000) {
  let processed = 0
  let lastId = null
  
  while (true) {
    const query = lastId ? {_id: {$gt: lastId}} : {}
    const batch = db.bigcollection.find(query)
      .sort({_id: 1})
      .limit(batchSize)
      .toArray()
    
    if (batch.length === 0) break
    
    // 处理批次数据
    batch.forEach(doc => {
      // 业务处理逻辑
      processDocument(doc)
    })
    
    lastId = batch[batch.length - 1]._id
    processed += batch.length
    
    console.log(`已处理 ${processed} 条记录`)
  }
}
13.8. 架构设计题
Q16: 如何设计MongoDB的备份和恢复策略?
A: MongoDB备份恢复最佳实践:
# 1. mongodump/mongorestore(适合小型数据库)
# 全库备份
mongodump --host localhost:27017 --out /backup/mongodb-$(date +%Y%m%d)
# 指定数据库备份
mongodump --host localhost:27017 --db myapp --out /backup/myapp-$(date +%Y%m%d)
# 恢复数据
mongorestore --host localhost:27017 --drop /backup/mongodb-20240807/
# 2. 文件系统快照(适合大型数据库)
# 停止写入或使用db.fsyncLock()
db.fsyncLock()
# 创建文件系统快照
sudo lvm snapshot /dev/vg0/mongodb-data
# 解除锁定
db.fsyncUnlock()
# 3. 复制集的备份策略
# 从Secondary节点备份,不影响Primary性能
mongodump --host secondary-host:27017 --oplog
# 4. 时间点恢复(Point-in-Time Recovery)
# 结合全量备份和oplog重放
mongorestore /backup/full-backup
mongorestore --oplogReplay /backup/oplog-replay
# 5. MongoDB Atlas的自动备份
# 云版本提供连续备份和时间点恢复
// 备份脚本示例
function createBackupStrategy() {
  return {
    // 每日全量备份
    daily: {
      schedule: "0 2 * * *", // 凌晨2点
      retention: "30d",      // 保留30天
      command: "mongodump --host replica-set/host1,host2,host3 --oplog"
    },
    
    // 每小时增量备份(oplog)
    hourly: {
      schedule: "0 * * * *", // 每小时
      retention: "7d",       // 保留7天
      command: "mongodump --host secondary --oplog --query '{ts:{$gt:ObjectId(\"...\")}}'"
    },
    
    // 周备份(归档)
    weekly: {
      schedule: "0 3 * * 0", // 周日凌晨3点
      retention: "12w",      // 保留12周
      storage: "offsite"     // 异地存储
    }
  }
}
Q17: 如何进行MongoDB的容量规划?
A: MongoDB容量规划考虑因素:
// 1. 数据增长预估
function calculateDataGrowth() {
  const currentData = db.stats()
  
  return {
    currentSize: currentData.dataSize,
    indexSize: currentData.indexSize,
    totalSize: currentData.storageSize,
    
    // 预估增长(基于历史数据)
    monthlyGrowthRate: 0.15, // 15%月增长
    yearlyProjection: currentData.dataSize * Math.pow(1.15, 12),
    
    // 考虑压缩比例
    compressionRatio: 0.7, // WiredTiger压缩比例
    actualStorageNeeded: currentData.dataSize * 0.7
  }
}
// 2. 性能需求评估
const performanceRequirements = {
  // 读写QPS需求
  readQPS: 10000,
  writeQPS: 2000,
  
  // 延迟要求
  maxReadLatency: 10,   // ms
  maxWriteLatency: 50,  // ms
  
  // 并发连接数
  maxConnections: 1000,
  
  // 可用性要求
  uptime: "99.9%",
  rto: 30, // Recovery Time Objective (seconds)
  rpo: 60  // Recovery Point Objective (seconds)
}
// 3. 硬件规划
const hardwareSpecs = {
  // CPU规划
  cpu: {
    cores: Math.ceil((performanceRequirements.readQPS + performanceRequirements.writeQPS) / 1000),
    frequency: "2.4GHz+",
    recommendation: "高频率比多核心更重要"
  },
  
  // 内存规划
  memory: {
    // 工作集大小 + 操作系统 + 连接开销
    workingSetSize: "50GB", // 热数据
    osOverhead: "8GB",
    connectionOverhead: performanceRequirements.maxConnections * 1, // 1MB per connection
    totalRAM: "64GB",
    recommendation: "工作集应完全放入内存"
  },
  
  // 存储规划
  storage: {
    dataSize: "500GB",
    indexSize: "100GB", 
    oplogSize: "50GB",   // 24-72小时的操作日志
    totalStorage: "1TB",
    type: "SSD",
    iops: "10000+",
    recommendation: "使用SSD,RAID 10配置"
  },
  
  // 网络规划
  network: {
    bandwidth: "1Gbps+",
    latency: "<1ms", // 集群内部
    recommendation: "专用网络,低延迟"
  }
}
// 4. 集群架构规划
const clusterArchitecture = {
  // 复制集配置
  replicaSet: {
    primary: 1,
    secondary: 2, // 至少2个
    arbiter: 0,   // 推荐使用数据节点代替仲裁节点
    hidden: 1,    // 用于备份和分析
    delayed: 1    // 延迟节点,防误删
  },
  
  // 分片配置(大数据量场景)
  sharding: {
    enabled: true,
    configServers: 3,  // 配置服务器复制集
    mongosRouters: 2,  // 路由器(应用层前面)
    shards: [
      {name: "shard1", replicas: 3},
      {name: "shard2", replicas: 3},
      {name: "shard3", replicas: 3}
    ],
    shardKey: {userId: "hashed"} // 分片键选择
  }
}
13.9. 故障排查题
Q18: MongoDB常见性能问题和解决方案?
A: MongoDB性能问题诊断和解决:
// 1. 慢查询诊断
db.setProfilingLevel(2, {slowms: 100})
// 分析慢查询
db.system.profile.find().limit(5).sort({ts: -1}).forEach(
  function(op) {
    print("命令: " + JSON.stringify(op.command))
    print("执行时间: " + op.millis + "ms")
    print("扫描文档数: " + op.docsExamined)
    print("返回文档数: " + op.docsReturned)
    print("---")
  }
)
// 2. 锁争用问题
db.serverStatus().globalLock.currentQueue // 查看锁队列
db.currentOp({"waitingForLock": true})     // 查看等待锁的操作
// 解决方案:
// - 优化查询,减少锁持有时间
// - 使用合适的索引
// - 考虑读写分离
// - 升级到WiredTiger存储引擎(文档级锁)
// 3. 内存压力问题
const memStats = db.serverStatus().mem
const wiredTiger = db.serverStatus().wiredTiger
console.log({
  resident: memStats.resident,      // 物理内存使用
  virtual: memStats.virtual,        // 虚拟内存使用
  cacheSize: wiredTiger.cache["bytes currently in the cache"],
  cacheDirty: wiredTiger.cache["bytes dirty in the cache"]
})
// 内存优化:
// - 增加物理内存
// - 调整WiredTiger缓存大小
// - 优化查询减少内存使用
// - 使用投影限制返回字段
// 4. 连接池问题
db.serverStatus().connections
// 解决方案:
// - 配置合适的连接池大小
// - 使用连接池监控
// - 检查连接泄漏
// 5. 磁盘I/O问题
db.serverStatus().wiredTiger.cache // 缓存命中率
db.serverStatus().wiredTiger["block-manager"] // I/O统计
// I/O优化:
// - 使用SSD存储
// - 增加内存提高缓存命中率
// - 优化数据模型减少I/O
// - 考虑数据压缩
Q19: 如何处理MongoDB的数据迁移?
A: MongoDB数据迁移策略:
// 1. 在线迁移(零停机)
// 使用Change Streams实现实时同步
const changeStream = db.sourceCollection.watch([
  {$match: {"fullDocument.status": "active"}}
])
changeStream.on('change', next => {
  switch(next.operationType) {
    case 'insert':
      db.targetCollection.insertOne(next.fullDocument)
      break
    case 'update':
      db.targetCollection.updateOne(
        {_id: next.documentKey._id},
        {$set: next.updateDescription.updatedFields}
      )
      break
    case 'delete':
      db.targetCollection.deleteOne({_id: next.documentKey._id})
      break
  }
})
// 2. 批量数据迁移
function migrateCollection(sourceCol, targetCol, batchSize = 1000) {
  let lastId = null
  let totalMigrated = 0
  
  while (true) {
    const query = lastId ? {_id: {$gt: lastId}} : {}
    const batch = sourceCol.find(query)
      .sort({_id: 1})
      .limit(batchSize)
      .toArray()
    
    if (batch.length === 0) break
    
    // 数据转换逻辑
    const transformedBatch = batch.map(doc => {
      return {
        ...doc,
        migratedAt: new Date(),
        version: "v2"
      }
    })
    
    // 批量插入
    targetCol.insertMany(transformedBatch)
    
    lastId = batch[batch.length - 1]._id
    totalMigrated += batch.length
    
    console.log(`迁移进度: ${totalMigrated} 条记录`)
  }
}
// 3. 跨集群迁移
// 使用mongodump/mongorestore
mongodump --host source-cluster --out /tmp/migration
mongorestore --host target-cluster /tmp/migration
// 4. 数据校验
function validateMigration(sourceCol, targetCol) {
  const sourceCount = sourceCol.countDocuments()
  const targetCount = targetCol.countDocuments()
  
  console.log(`源集合文档数: ${sourceCount}`)
  console.log(`目标集合文档数: ${targetCount}`)
  
  if (sourceCount !== targetCount) {
    console.log("⚠️ 文档数量不一致!")
  }
  
  // 抽样验证数据一致性
  const sampleDocs = sourceCol.aggregate([{$sample: {size: 100}}])
  
  sampleDocs.forEach(doc => {
    const targetDoc = targetCol.findOne({_id: doc._id})
    if (!targetDoc) {
      console.log(`❌ 缺失文档: ${doc._id}`)
    }
  })
}
14. 总结
MongoDB作为领先的NoSQL数据库,在现代应用开发中扮演着重要角色。通过本指南的学习,你应该能够:
14.1. 掌握的核心技能:
- 基础操作:CRUD操作、索引管理、聚合查询
 - 高级特性:复制集、分片、事务处理
 - 性能优化:查询优化、索引策略、监控诊断
 - 架构设计:数据建模、容量规划、高可用设计
 
14.2. 实际应用能力:
- 设计高性能的数据库架构
 - 处理大数据量的查询和存储
 - 实现高可用和故障恢复
 - 进行性能调优和问题排查
 
14.3. 面试准备:
- 理解MongoDB的核心概念和原理
 - 掌握常见问题的解决方案
 - 具备实际项目经验和最佳实践
 
继续学习建议:
- 多做实践项目,加深理解
 - 关注MongoDB官方文档和社区动态
 - 学习相关生态工具(如MongoDB Compass、Atlas等)
 - 了解与其他技术的集成(如微服务、大数据分析等)
 
MongoDB的学习是一个持续的过程,随着技术的发展和项目需求的变化,需要不断更新知识和技能。希望这个指南能为你的MongoDB学习之旅提供有价值的参考!