后端工程Technical Deep Dive

MongoDB 实战:文档数据库应用场景

发布时间2026/03/29
分类后端工程
预计阅读8 分钟
作者吴长龙
*

MongoDB 实战:文档数据库应用场景

01.内容

# MongoDB 实战:文档数据库应用场景

MongoDB 是最流行的文档数据库,其灵活的 Schema 非常适合快速迭代的业务场景。本文介绍 MongoDB 的核心概念、进阶操作以及与 PostgreSQL 的选型建议。

02.一、核心概念

1.1 与关系型数据库对比

关系型MongoDB
DatabaseDatabase
TableCollection
RowDocument
ColumnField
JOIN$lookup / 嵌入文档
PRIMARY KEY_id

1.2 文档结构

javascript snippetjavascript
// MongoDB 文档(BSON)
{
  _id: ObjectId("5f47a1b..."),
  name: "张三",
  email: "zhangsan@example.com",
  profile: {
    age: 28,
    city: "北京",
    tags: ["vip", "developer"]
  },
  orders: [
    { orderId: 1, amount: 100 },
    { orderId: 2, amount: 200 }
  ],
  createdAt: ISODate("2026-01-01T00:00:00Z"),
  status: "active"
}

03.二、基本操作

2.1 CRUD

javascript snippetjavascript
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('mydb');
const users = db.collection('users');

// 插入
await users.insertOne({ name: '张三', email: 'zhangsan@example.com' });
await users.insertMany([
  { name: '李四', email: 'lisi@example.com' },
  { name: '王五', email: 'wangwu@example.com' }
]);

// 查询
await users.find({ status: 'active' }).toArray();
await users.findOne({ _id: new ObjectId("...") });

// 条件查询
await users.find({ 
  age: { $gte: 18, $lte: 35 },
  status: 'active',
  tags: { $in: ['vip', 'premium'] }
}).sort({ createdAt: -1 }).skip(20).limit(10);

// 更新
await users.updateOne(
  { _id: new ObjectId("...") },
  { $set: { name: '张三新' }, $inc: { age: 1 } }
);

// 删除
await users.deleteOne({ _id: new ObjectId("...") });

2.2 聚合管道

javascript snippetjavascript
// 聚合管道:数据处理流水线
const pipeline = [
  // 1. 筛选
  { $match: { status: 'active', createdAt: { $gte: new Date('2026-01-01') } } },
  
  // 2. 展开数组
  { $unwind: '$orders' },
  
  // 3. 分组统计
  { $group: {
    _id: '$userId',
    totalAmount: { $sum: '$orders.amount' },
    orderCount: { $sum: 1 }
  }},
  
  // 4. 排序
  { $sort: { totalAmount: -1 } },
  
  // 5. 限制
  { $limit: 10 },
  
  // 6. 投影
  { $project: {
    userId: '$_id',
    totalAmount: 1,
    orderCount: 1,
    _id: 0
  }}
];

const results = await users.aggregate(pipeline).toArray();

2.3 索引

javascript snippetjavascript
// 单字段索引
await users.createIndex({ email: 1 }, { unique: true });

// 复合索引(顺序重要)
await users.createIndex({ status: 1, createdAt: -1 });

// 多键索引(数组字段)
await users.createIndex({ tags: 1 });

// 文本索引(搜索)
await posts.createIndex({ title: 'text', content: 'text' });
await posts.find({ $text: { $search: 'MongoDB 教程' } });

// 查看查询计划
await users.find({ status: 'active' }).explain();

04.三、进阶模式

3.1 文档嵌入 vs 引用

javascript snippetjavascript
// 嵌入:数据经常一起查询
{
  name: "订单1",
  items: [
    { productId: 1, name: "商品A", price: 100 },
    { productId: 2, name: "商品B", price: 200 }
  ]
}

// 引用:数据独立更新、很大
{
  orderId: 1,
  userId: ObjectId("..."),  // 引用
  items: [
    { productId: ObjectId("..."), quantity: 1 }
  ]
}

// 查询时使用 $lookup
const orders = await db.collection('orders').aggregate([
  {
    $lookup: {
      from: 'users',
      localField: 'userId',
      foreignField: '_id',
      as: 'user'
    }
  },
  { $unwind: '$user' },
  { $project: { 'user.password': 0 } }  // 排除敏感字段
]).toArray();

3.2 变更流(Change Stream)

javascript snippetjavascript
// 监听数据变更
const changeStream = db.collection('users').watch();

changeStream.on('change', (change) => {
  switch (change.operationType) {
    case 'insert':
      console.log('新增:', change.fullDocument);
      break;
    case 'update':
      console.log('更新:', change.updateDescription);
      break;
    case 'delete':
      console.log('删除:', change.documentKey);
      break;
  }
});

// 用于微服务同步、实时推送等场景

05.四、事务支持

javascript snippetjavascript
// MongoDB 4.0+ 支持多文档事务
const session = client.startSession();

try {
  await session.withTransaction(async () => {
    const users = db.collection('users');
    const accounts = db.collection('accounts');
    
    // 转账
    await accounts.updateOne(
      { userId: fromUserId },
      { $inc: { balance: -amount } },
      { session }
    );
    
    await accounts.updateOne(
      { userId: toUserId },
      { $inc: { balance: amount } },
      { session }
    );
    
    // 记录日志
    await db.collection('transactions').insertOne({
      from: fromUserId,
      to: toUserId,
      amount,
      timestamp: new Date()
    }, { session });
  });
} finally {
  await session.endSession();
}

06.五、选型建议

5.1 何时选择 MongoDB

场景原因
快速迭代的产品Schema 灵活,无需迁移
日志/分析数据写入量大,结构多样
用户行为数据嵌套文档,减少 JOIN
内容管理系统文档即文章,天然匹配
物联网数据时间序列文档

5.2 何时选择 PostgreSQL

场景原因
强一致性要求ACID 事务
复杂查询SQL 成熟稳定
财务报表精确数值
关系复杂JOIN 查询
团队熟悉生态成熟

5.3 混合使用

javascript snippetjavascript
// 常见模式:MongoDB + PostgreSQL
// - PostgreSQL: 用户、订单、支付等核心业务
// - MongoDB: 用户行为日志、内容管理、缓存数据

// 或者:同一个应用,不同服务选择不同数据库
// 用户服务 -> PostgreSQL
// 日志服务 -> MongoDB

07.总结

特性MongoDBPostgreSQL
Schema灵活(无模式)固定
JOIN有限完整支持
事务4.0+ 多文档原生 ACID
性能(写入)
社区

建议:如果是新项目且不确定数据模型,用 MongoDB 快速开发;业务稳定后,可考虑迁移到 PostgreSQL 获取更强的查询能力。

---

*后续将介绍容器化与部署实战。*