MongoDB 入门详解

创建时间.png 2025-12-15
标签.png tool
阅读量.png 33

前言

在构建现代 Web 应用时,我们经常需要在关系型数据库(MySQL、PostgreSQL)和文档型数据库(MongoDB)之间做选择。MongoDB 以其灵活的文档模型、强大的查询能力和水平扩展特性,成为许多开发者的首选。本篇文章将带你从零开始掌握 MongoDB 的核心用法

一、MongoDB 基础概念

1.1 解决了什么问题?

  • 关系型数据库需要预定义表结构,字段变更成本高。
  • 某些业务场景(如用户画像、内容管理)需要灵活的数据结构,不同文档可能有不同字段。
  • MongoDB 采用 BSON(Binary JSON) 格式存储文档,无需固定 schema,支持嵌套对象和数组。

1.2 核心概念对比

概念 关系型数据库 MongoDB
数据库 Database Database
Table Collection(集合)
Row Document(文档)
Column Field(字段)
主键 Primary Key _id(自动生成)
  • Collection(集合):类似于表,但无需预定义结构。
  • Document(文档):类似于行,但可以包含嵌套对象和数组。
  • Field(字段):文档中的键值对,支持多种数据类型。

二、安装与连接

2.1 本地安装

macOS(使用 Homebrew):

brew tap mongodb/brew
brew install mongodb-community
brew services start mongodb-community

Docker 方式(推荐):

docker run -d -p 27017:27017 --name mongodb mongo:latest

2.2 连接字符串格式

mongodb://[username:password@]host[:port][/database][?options]

示例:

mongodb://localhost:27017/myapp
mongodb://user:pass@localhost:27017/myapp?authSource=admin
mongodb+srv://user:pass@cluster.mongodb.net/myapp
  • 默认端口:27017
  • 无认证时可直接连接本地实例
  • MongoDB Atlas(云服务)使用 mongodb+srv:// 协议

三、Node.js 连接实战

使用 mongoose 库(最流行的 MongoDB ODM):

import mongoose from 'mongoose';

// 连接 MongoDB
const connectDB = async () => {
  try {
    await mongoose.connect('mongodb://localhost:27017/myapp', {
      useNewUrlParser: true,
      useUnifiedTopology: true,
    });
    console.log('MongoDB 连接成功');
  } catch (error) {
    console.error('连接失败:', error);
    process.exit(1);
  }
};

connectDB();

// 定义 Schema(数据模型)
const userSchema = new mongoose.Schema({
  name: { type: String, required: true },
  email: { type: String, unique: true, required: true },
  age: { type: Number, min: 0 },
  tags: [String],
  createdAt: { type: Date, default: Date.now },
});

// 创建 Model
const User = mongoose.model('User', userSchema);
  • mongoose.connect() 返回 Promise,支持 async/await
  • Schema 定义字段类型和验证规则
  • Model 提供数据库操作方法

四、CRUD 操作详解

4.1 创建(Create)

// 单条插入
const newUser = await User.create({
  name: '张三',
  email: 'zhangsan@example.com',
  age: 25,
  tags: ['前端', 'Node.js'],
});

// 或使用 save()
const user = new User({ name: '李四', email: 'lisi@example.com' });
await user.save();

// 批量插入
await User.insertMany([
  { name: '王五', email: 'wangwu@example.com' },
  { name: '赵六', email: 'zhaoliu@example.com' },
]);

4.2 查询(Read)

// 查询所有
const users = await User.find();

// 条件查询
const user = await User.findOne({ email: 'zhangsan@example.com' });
const adults = await User.find({ age: { $gte: 18 } });

// 投影(只返回指定字段)
const names = await User.find({}, 'name email');

// 排序与分页
const users = await User.find()
  .sort({ createdAt: -1 })
  .skip(10)
  .limit(20);

// 复杂查询
const result = await User.find({
  age: { $gte: 18, $lte: 65 },
  tags: { $in: ['前端', '后端'] },
  name: { $regex: /张/, $options: 'i' },
});

常用查询操作符:

  • $gt$gte$lt$lte:比较
  • $in$nin:包含/不包含
  • $regex:正则匹配
  • $exists:字段存在性
  • $or$and:逻辑运算

4.3 更新(Update)

// 更新单条(返回更新后的文档)
const updated = await User.findOneAndUpdate(
  { email: 'zhangsan@example.com' },
  { age: 26 },
  { new: true } // 返回更新后的文档
);

// 更新多条
await User.updateMany(
  { age: { $lt: 18 } },
  { $set: { status: 'minor' } }
);

// 使用操作符
await User.updateOne(
  { _id: userId },
  { 
    $inc: { age: 1 },           // 自增
    $push: { tags: '新标签' },   // 数组追加
    $set: { updatedAt: Date.now() }
  }
);

常用更新操作符:

  • $set:设置字段值
  • $unset:删除字段
  • $inc:数值增减
  • $push$pull:数组操作
  • $addToSet:数组去重追加

4.4 删除(Delete)

// 删除单条
await User.deleteOne({ email: 'zhangsan@example.com' });

// 删除多条
await User.deleteMany({ age: { $lt: 18 } });

// 查找并删除(返回被删除的文档)
const deleted = await User.findOneAndDelete({ _id: userId });

五、聚合查询(Aggregation)

聚合管道是 MongoDB 最强大的功能之一,类似 SQL 的 GROUP BY:

// 统计各年龄段用户数量
const stats = await User.aggregate([
  { $match: { age: { $exists: true } } },  // 过滤
  { $group: {
    _id: { $switch: {
      branches: [
        { case: { $lt: ['$age', 18] }, then: '未成年' },
        { case: { $lt: ['$age', 30] }, then: '青年' },
        { case: { $lt: ['$age', 50] }, then: '中年' },
        { case: true, then: '老年' }
      ],
      default: '未知'
    }},
    count: { $sum: 1 },
    avgAge: { $avg: '$age' }
  }},
  { $sort: { count: -1 } }
]);

// 查找包含特定标签的用户,并展开标签数组
const result = await User.aggregate([
  { $match: { tags: { $exists: true, $ne: [] } } },
  { $unwind: '$tags' },
  { $group: {
    _id: '$tags',
    users: { $push: '$name' }
  }}
]);

常用聚合阶段:

  • $match:过滤文档
  • $group:分组统计
  • $project:字段投影
  • $sort:排序
  • $limit$skip:分页
  • $unwind:展开数组
  • $lookup:关联查询(类似 JOIN)

六、索引优化

索引能大幅提升查询性能:

// 创建单字段索引
await User.createIndex({ email: 1 }); // 1 升序,-1 降序

// 创建复合索引
await User.createIndex({ name: 1, age: -1 });

// 创建文本索引(支持全文搜索)
await User.createIndex({ name: 'text', bio: 'text' });

// 查看索引
const indexes = await User.collection.getIndexes();

// 删除索引
await User.collection.dropIndex('email_1');

索引策略建议:

  1. 频繁查询的字段创建索引
  2. 唯一字段(如 email)创建唯一索引
  3. 复合索引遵循"最左前缀"原则
  4. 避免过多索引,影响写入性能
  5. 使用 explain() 分析查询计划
// 分析查询性能
const explain = await User.find({ email: 'test@example.com' }).explain();
console.log(explain.executionStats);

七、实战示例:博客文章系统

结合《博客搭建二-文章接口》,用 MongoDB 实现文章管理:

import mongoose from 'mongoose';

// 文章 Schema
const articleSchema = new mongoose.Schema({
  title: { type: String, required: true, index: true },
  content: { type: String, required: true },
  author: { type: mongoose.Schema.Types.ObjectId, ref: 'User', required: true },
  tags: [String],
  views: { type: Number, default: 0 },
  published: { type: Boolean, default: false },
  publishedAt: Date,
  createdAt: { type: Date, default: Date.now },
  updatedAt: { type: Date, default: Date.now },
});

// 自动更新 updatedAt
articleSchema.pre('save', function(next) {
  this.updatedAt = Date.now();
  next();
});

const Article = mongoose.model('Article', articleSchema);

// 创建文章
const createArticle = async (data) => {
  return await Article.create({
    ...data,
    publishedAt: data.published ? Date.now() : null,
  });
};

// 查询已发布文章(带作者信息)
const getPublishedArticles = async (page = 1, limit = 10) => {
  return await Article.find({ published: true })
    .populate('author', 'name email') // 关联查询用户信息
    .sort({ publishedAt: -1 })
    .skip((page - 1) * limit)
    .limit(limit);
};

// 增加浏览量
const incrementViews = async (articleId) => {
  return await Article.findByIdAndUpdate(
    articleId,
    { $inc: { views: 1 } },
    { new: true }
  );
};

// 按标签统计文章数
const getTagStats = async () => {
  return await Article.aggregate([
    { $match: { published: true } },
    { $unwind: '$tags' },
    { $group: {
      _id: '$tags',
      count: { $sum: 1 },
      articles: { $push: { title: '$title', id: '$_id' } }
    }},
    { $sort: { count: -1 } }
  ]);
};

八、性能优化与最佳实践

  1. 连接池管理

    mongoose.connect(uri, {
    maxPoolSize: 10,        // 最大连接数
    minPoolSize: 5,        // 最小连接数
    serverSelectionTimeoutMS: 5000,
    });
  2. 批量操作优化

    // 使用 bulkWrite 替代多次 updateOne
    await User.bulkWrite([
    { updateOne: { filter: { _id: id1 }, update: { $set: { status: 'active' } } } },
    { updateOne: { filter: { _id: id2 }, update: { $set: { status: 'inactive' } } } },
    ]);
  3. 查询优化

  • 使用 select() 只查询需要的字段
  • 避免 $regex 在大量数据上的全表扫描
  • 合理使用 lean() 返回纯 JavaScript 对象(不创建 Mongoose 文档实例)
const users = await User.find().lean(); // 性能更好,但失去 Mongoose 方法
  1. 数据验证
    const userSchema = new mongoose.Schema({
    email: {
     type: String,
     required: true,
     unique: true,
     validate: {
       validator: (v) => /^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$/.test(v),
       message: '邮箱格式不正确'
     }
    }
    });

九、常见排障清单

  • MongoServerError: E11000 duplicate key error

    • 检查唯一索引字段是否有重复值,或删除冲突的索引后重建。
  • MongooseError: Operationusers.findOne()buffering timed out after 10000ms

    • 确认 MongoDB 服务已启动,检查连接字符串是否正确。
  • 查询性能慢

    • 使用 explain() 分析查询计划,检查是否使用了索引。
    • 考虑添加复合索引或优化查询条件。
  • CastError: Cast to ObjectId failed

    • 确保传入的 _id 是有效的 MongoDB ObjectId 格式(24 位十六进制字符串)。
  • 内存占用过高

    • 使用 lean() 查询,避免创建大量 Mongoose 文档实例。
    • 限制查询结果数量,使用分页。

总结

  • MongoDB 是文档型数据库,适合灵活的数据结构和快速迭代。
  • Schema 定义提供数据验证,但 MongoDB 本身支持无结构存储。
  • 索引是性能优化的关键,合理使用单字段、复合和文本索引。
  • 聚合管道功能强大,能处理复杂的统计和分析需求。
  • 在 Node.js 中使用 mongoose 可以简化操作,但要注意连接池和查询优化。
  • 生产环境建议使用 MongoDB Atlas 或配置副本集,保证高可用性。

掌握 MongoDB 的核心用法和优化技巧,你就能在项目中灵活选择数据存储方案,构建高性能、可扩展的 Web 应用。

原文地址:https://webfem.com/post/mongodb-usage-guide,转载请注明出处

webfem 头像
webfem 博客
🧰 最新工具