数据库05:MongoDB

从文档模型、副本集、分片集群到聚合管道,理解 MongoDB 在非结构化数据存储场景下的设计思路和适用边界。

字数 1130 阅读时长 ≈ 4 分钟 2026-7-14 2026-7-14
数据库05:MongoDB

MongoDB 是最流行的 NoSQL 数据库之一,它的文档模型特别适合存储半结构化数据。很多项目用它来存储用户行为、日志、配置等数据。

这篇文章围绕四个核心点:文档模型、副本集、分片集群、聚合管道,理解 MongoDB 的设计思路和适用边界。

文档模型:为什么需要 NoSQL

传统关系型数据库的表结构是固定的,不适合存储结构多变的数据。MongoDB 的文档模型解决了这个问题。

文档 vs 行

维度关系型数据库MongoDB
数据结构固定表结构动态文档
数据类型基础类型支持嵌套、数组
关系外键关联嵌入式文档
扩展性需要 ALTER TABLE无 schema,灵活扩展

嵌入式文档 vs 引用

MongoDB 有两种方式处理关联数据:

嵌入式文档

{
  _id: ObjectId("..."),
  name: "旋大爷",
  address: {
    city: "北京",
    street: "中关村"
  },
  orders: [
    { orderId: "O001", amount: 100 },
    { orderId: "O002", amount: 200 }
  ]
}

引用方式

// 用户文档
{
  _id: ObjectId("..."),
  name: "旋大爷",
  orderIds: [ObjectId("..."), ObjectId("...")]
}

// 订单文档
{
  _id: ObjectId("..."),
  userId: ObjectId("..."),
  amount: 100
}

选择建议

场景嵌入式文档引用方式
数据一起读取
数据独立更新
一对多关系✓(文档大小 < 16MB)✓(大量子文档)
多对多关系

副本集:高可用的基础

MongoDB 通过副本集实现高可用,副本集由一个主节点和多个从节点组成。

副本集架构

Primary(主节点)
  ├── Secondary(从节点1)
  ├── Secondary(从节点2)
  └── Arbiter(仲裁节点)

选举机制

当主节点宕机时,副本集会自动选举新的主节点:

  1. 从节点检测到主节点不可达
  2. 触发选举,从节点投票
  3. 获得多数票的从节点成为新主节点

选举条件

  • 副本集必须有奇数个节点(保证多数派)
  • 从节点必须同步到最新数据
  • 从节点必须在选举超时时间内响应

读写策略

模式一致性
Primary Preferred优先读主节点写主节点强一致性
Secondary Preferred优先读从节点写主节点最终一致性
Nearest读最近节点写主节点最终一致性

分片集群:水平扩展的关键

当单节点数据量太大时,需要分片集群来水平扩展。

分片集群组件

  1. Shard:分片,存储数据的节点
  2. Config Server:配置服务器,存储集群元数据
  3. Mongos:路由服务器,负责查询路由

分片策略

策略方式优点缺点
范围分片按字段范围拆分范围查询高效数据分布不均匀
Hash 分片按字段哈希值拆分数据均匀分布范围查询低效

分片键选择

选择分片键需要考虑:

  • 基数:区分度高的字段(如 userId)
  • 写入分布:避免热点写入
  • 查询模式:常见查询条件能命中分片

反模式

  • 使用单调递增字段(如时间戳)→ 热点写入
  • 使用区分度低的字段(如性别)→ 数据分布不均

跨分片查询

  • 路由查询:查询条件包含分片键,直接路由到对应分片
  • 广播查询:查询条件不包含分片键,需要查询所有分片

聚合管道:数据处理的利器

MongoDB 的聚合管道是处理复杂数据的强大工具,可以完成数据转换、分组、排序等操作。

聚合管道阶段

db.orders.aggregate([
  // 阶段1:过滤
  { $match: { status: "completed" } },
  
  // 阶段2:分组
  { 
    $group: { 
      _id: "$userId", 
      totalAmount: { $sum: "$amount" },
      orderCount: { $count: {} }
    } 
  },
  
  // 阶段3:排序
  { $sort: { totalAmount: -1 } },
  
  // 阶段4:限制
  { $limit: 10 }
])

常用聚合操作

操作功能示例
$match过滤{ $match: { status: "completed" } }
$group分组{ $group: { _id: "$userId", total: { $sum: "$amount" } } }
$sort排序{ $sort: { total: -1 } }
$limit限制{ $limit: 10 }
$project投影{ $project: { name: 1, total: 1 } }
$lookup关联{ $lookup: { from: "users", localField: "userId", foreignField: "_id", as: "user" } }

Map-Reduce

对于复杂的数据处理,可以使用 Map-Reduce:

db.orders.mapReduce(
  function() { emit(this.userId, this.amount); },
  function(key, values) { return Array.sum(values); },
  {
    query: { status: "completed" },
    out: "user_totals"
  }
)

索引设计:加速查询

MongoDB 的索引设计和关系型数据库类似,但也有一些差异。

索引类型

类型说明适用场景
单字段索引单个字段等值查询、排序
复合索引多个字段多条件查询
数组索引数组中的每个元素数组元素查询
文本索引全文搜索搜索场景
地理空间索引地理位置地图应用
TTL 索引自动过期日志、缓存

索引使用原则

  1. 为常用查询条件创建索引
  2. 复合索引遵循最左前缀原则
  3. 避免创建过多索引(影响写入性能)
  4. 使用 explain() 分析查询计划

项目判断清单

  • 需要存储半结构化数据 → MongoDB
  • 需要灵活扩展数据结构 → MongoDB
  • 需要高并发写入 → MongoDB(配合分片)
  • 需要事务支持 → MongoDB 4.0+(多文档事务)
  • 需要复杂查询 → 考虑 PostgreSQL 或 Elasticsearch
  • 数据量超过单节点限制 → 分片集群
  • 需要自动过期数据 → TTL 索引