Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← Web 开发 / Web Development

数据库与数据访问 / Databases & Data Access

1. 数据库全景学习路线 / A Complete Database Learning Path

2. MySQL 实战——互联网标配关系型数据库 / Practical MySQL for Internet Applications

3. PostgreSQL 深度——功能最全的开源 RDBMS / PostgreSQL as a Feature-Rich Open-Source RDBMS

4. SQLite 嵌入式——零配置的极致轻量 / SQLite as a Lightweight Embedded Database

5. MongoDB 文档型数据库——灵活结构的代表 / MongoDB and Flexible Document Data Models

6. Redis 全方位——不只是缓存 / Redis Beyond Caching

7. 嵌入式 KV 数据库——基础设施层的隐形支柱 / Embedded Key-Value Databases for Infrastructure

8. NewSQL 分布式数据库——规模化关系型数据 / Distributed SQL for Relational Data at Scale

9. 向量数据库——AI 时代的语义基础设施 / Vector Databases as Semantic Infrastructure for AI

10. OLAP 列式数据库——极速分析引擎 / Columnar OLAP Databases for Fast Analytics

11. 时序数据库——时间线数据的专业存储 / Time-Series Databases for Timeline Data

12. 数据库选型决策指南——从理论到实践 / A Practical Guide to Database Selection

13. ORM 与 Prisma 完全指南 / A Complete Guide to ORMs and Prisma

本页目录

MongoDB 文档型数据库——灵活结构的代表 / MongoDB and Flexible Document Data Models ​

📅 创建时间:2026-05-08 🏷️ 标签:#MongoDB #文档型 #AggregationPipeline #副本集 #mongoose #Schema-less 📚 前置知识:[[00-db-overview]] [[01-mysql]](了解关系型数据库基础) 📚 相关知识:[[06-kv-embedded]](NoSQL 家族对比)


MongoDB 定位速览 ​

┌─────────────────────────────────────────────────────────────┐
│                MongoDB 在数据库版图中的位置                    │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  GitHub Stars: 26K+,全球最流行的文档型数据库                │
│  定位:      Schema-less 的 JSON 文档数据库                  │
│  核心哲学:  "数据应该像 JavaScript 对象一样灵活"            │
│  适用场景:  内容管理、UGC、快速迭代的产品、无固定结构的数据  │
│  不适合:    强关联/强事务场景、需要复杂 JOIN 的分析场景      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11

第1部分:文档模型——思维转换 ​

1.1 关系型 vs 文档型 ​

┌─────────────────────────────────────────────────────────────┐
│              关系型 vs 文档型思维差异                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  MySQL(关系型):                                          │
│                                                             │
│  users ──1:N──► orders ──N:1──► products                   │
│                                                             │
│  3 张表 + JOIN + 外键约束                                  │
│  优点:结构清晰、事务完整、JOIN 强大                         │
│  缺点:改结构代价大、JSON 数据要拆成多表                     │
│                                                             │
│  MongoDB(文档型):                                        │
│                                                             │
│  users 文档:                                              │
│  {                                                         │
│    _id: ObjectId,                                          │
│    name: "张三",                                          │
│    orders: [                                                │
│      {                                                     │
│        order_id: "ORD001",                                 │
│        items: [                                             │
│          { name: "手机", price: 5000 }                     │
│        ],                                                  │
│        total: 5000                                         │
│      }                                                     │
│    ]                                                       │
│  }                                                         │
│                                                             │
│  一张表 + 内嵌文档 + 数组                                   │
│  优点:灵活扩展、一次查询获取完整对象                        │
│  缺点:无 JOIN、需要反规范化、事务支持较弱                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

1.2 MongoDB 的 JSON 文档 ​

javascript
// MongoDB 文档(BSON 格式,类 JSON 的二进制格式)
{
    "_id": ObjectId("507f1f77bcf86cd799439011"),
    "username": "zhangsan",
    "profile": {
        "age": 28,
        "email": "zhang@example.com",
        "tags": ["程序员", "猫奴", "羽毛球"],  // 数组
        "address": {
            "city": "北京",
            "district": "海淀区"
        }
    },
    "orders": [
        {
            "orderId": "ORD20260508001",
            "items": [
                { "name": "键盘", "qty": 1, "price": 299 },
                { "name": "鼠标", "qty": 2, "price": 89 }
            ],
            "total": 477,
            "createdAt": ISODate("2026-05-08T10:30:00Z")
        }
    ],
    "status": "active",
    "createdAt": ISODate("2025-01-01T00:00:00Z")
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

第2部分:核心操作 ​

2.1 CRUD 操作 ​

javascript
// 连接 MongoDB
const { MongoClient } = require('mongodb');
const client = new MongoClient('mongodb://localhost:27017');
const db = client.db('myapp');

// ================== 插入 ==================
// 单条插入
await db.collection('users').insertOne({
    name: '张三',
    age: 28,
    tags: ['程序员', '羽毛球'],
    createdAt: new Date()
});

// 批量插入
await db.collection('products').insertMany([
    { name: '键盘', price: 299, stock: 100, category: '外设' },
    { name: '鼠标', price: 89,  stock: 200, category: '外设' },
    { name: '显示器', price: 1999, stock: 50, category: '配件' }
]);

// ================== 查询 ==================
// 基本查询
const user = await db.collection('users').findOne({ name: '张三' });

// 条件查询
const products = await db.collection('products').find({
    price: { $gt: 100, $lt: 1000 },  // 100 < price < 1000
    category: '外设',
    stock: { $gte: 10 }              // stock >= 10
}).toArray();

// 分页查询
const page3 = await db.collection('products')
    .find({ category: '外设' })
    .sort({ price: 1 })               // 按价格升序
    .skip(20)                         // 跳过前 20 条
    .limit(10)                        // 取 10 条
    .toArray();

// 投影(只返回特定字段)
const names = await db.collection('products')
    .find({}, { projection: { name: 1, price: 1 } })  // 只返回 name 和 price
    .toArray();

// ================== 更新 ==================
// 原子更新
await db.collection('users').updateOne(
    { name: '张三' },
    {
        $inc: { age: 1 },              // age 自增 1
        $set: { updatedAt: new Date() },
        $push: { tags: '新手' }         // 数组追加
    }
);

// 替换整个文档
await db.collection('users').replaceOne(
    { name: '张三' },
    { name: '张三', age: 29, tags: ['程序员'] }
);

// ================== 删除 ==================
await db.collection('users').deleteOne({ name: '张三' });
await db.collection('products').deleteMany({ stock: 0 });  // 删除库存为 0 的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

2.2 常用查询操作符 ​

javascript
// 比较操作符
{ price: { $eq: 100 } }           // = 100(简写:{price: 100})
{ price: { $ne: 100 } }           // != 100
{ price: { $gt: 100 } }           // > 100
{ price: { $gte: 100 } }          // >= 100
{ price: { $lt: 100 } }           // < 100
{ price: { $lte: 100 } }          // <= 100
{ price: { $in: [99, 199, 299] } } // IN [99, 199, 299]
{ price: { $nin: [99, 199] } }    // NOT IN

// 逻辑操作符
{ $and: [{ price: { $gt: 100 } }, { stock: { $gt: 0 } }] }
{ $or: [{ price: { $lt: 100 } }, { name: '键盘' }] }
{ $not: { price: { $gt: 1000 } } }

// 数组操作符
{ tags: '程序员' }                // 数组包含"程序员"
{ tags: { $all: ['程序员', '羽毛球'] } }  // 同时包含两者
{ tags: { $size: 2 } }           // 数组长度为 2
{ 'profile.address.city': '北京' } // 嵌套字段

// 字符串操作符
{ name: { $regex: '^键', $options: 'i' } }  // 正则匹配(i=忽略大小写)
{ name: { $regex: '键盘|鼠标' } }            // 或匹配

// 存在性
{ email: { $exists: true, $ne: null } }  // 存在且非 null
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

第3部分:Aggregation Pipeline ​

聚合管道是 MongoDB 最强大的功能:

javascript
// 聚合管道:将多个处理阶段串联起来
// 等价于 SQL: SELECT category, COUNT(*), AVG(price)
//            FROM products GROUP BY category

const stats = await db.collection('products').aggregate([
    // Stage 1: 过滤(相当于 WHERE)
    { $match: { stock: { $gt: 0 } } },

    // Stage 2: 分组(相当于 GROUP BY)
    { $group: {
        _id: '$category',
        count: { $sum: 1 },
        avgPrice: { $avg: '$price' },
        totalStock: { $sum: '$stock' },
        cheapest: { $min: '$price' },
        mostExpensive: { $max: '$price' }
    }},

    // Stage 3: 排序(相当于 ORDER BY)
    { $sort: { totalStock: -1 } },

    // Stage 4: 投影(相当于 SELECT)
    { $project: {
        _id: 0,               // 隐藏 _id
        category: '$_id',     // 重命名
        count: 1,
        avgPrice: { $round: ['$avgPrice', 2] },  // 保留 2 位小数
        totalStock: 1
    }}
]).toArray();

console.log(stats);
// [ { category: '外设', count: 2, avgPrice: 194, totalStock: 300 }, ... ]

// 实战:统计用户月订单额
const monthlyRevenue = await db.collection('orders').aggregate([
    { $match: { status: 'completed', createdAt: { $gte: new Date('2026-01-01') } } },

    // 展开订单商品数组
    { $unwind: '$items' },

    // 计算每件商品小计
    { $addFields: {
        itemTotal: { $multiply: ['$items.qty', '$items.price'] }
    }},

    // 按月分组
    { $group: {
        _id: {
            year: { $year: '$createdAt' },
            month: { $month: '$createdAt' }
        },
        revenue: { $sum: '$itemTotal' },
        orderCount: { $sum: 1 }
    }},

    { $sort: { '_id.year': 1, '_id.month': 1 } }
]).toArray();

// 实战:用户购买力分层
const userTiers = await db.collection('orders').aggregate([
    { $group: {
        _id: '$userId',
        totalSpent: { $sum: '$total' }
    }},
    { $bucket: {
        groupBy: '$totalSpent',
        boundaries: [0, 1000, 5000, 10000, Infinity],
        default: '其他',
        output: {
            count: { $sum: 1 },
            users: { $push: '$_id' }
        }
    }}
]).toArray();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

第4部分:索引 ​

javascript
// 创建索引
await db.collection('users').createIndex({ email: 1 }, { unique: true });    // 唯一索引
await db.collection('products').createIndex({ price: 1, stock: -1 });         // 复合索引
await db.collection('products').createIndex({ name: 'text' });                // 文本索引
await db.collection('users').createIndex({ 'profile.address.city': 1 } );   // 嵌套字段索引

// 地理空间索引
await db.collection('stores').createIndex({ location: '2dsphere' });
await db.collection('stores').find({
    location: {
        $nearSphere: {
            $geometry: { type: 'Point', coordinates: [116.45, 39.93] },
            $maxDistance: 5000  // 5 公里
        }
    }
}).toArray();

// 查看查询计划(相当于 EXPLAIN)
const explain = await db.collection('products')
    .find({ price: { $gt: 100 }, category: '外设' })
    .explain('executionStats');
console.log(explain.executionStats.totalDocsExamined, explain.executionStats.nReturned);

// 列出所有索引
await db.collection('users').indexes();
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

第5部分:副本集与分片 ​

5.1 副本集(Replica Set) ​

┌─────────────────────────────────────────────────────────────┐
│                    MongoDB 副本集架构                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│                  Primary (主节点)                            │
│                  接受所有写操作                              │
│                     │ ▲                                     │
│            同步 ── │ ── 复制                               │
│                     ▼ │                                     │
│         ┌───────────┴───────────┐                         │
│         │                       │                          │
│    Secondary                Secondary                       │
│    (从节点1)                (从节点2)                       │
│    可读/不可写              可读/不可写                      │
│                                                             │
│  副本集选举:                                              │
│  • Primary 宕机 → 剩余节点自动投票 → 新 Primary             │
│  • 默认 3 节点(1 Primary + 2 Secondary)                 │
│  • 建议奇数节点(避免脑裂)                                 │
│                                                             │
│  读写分离:                                               │
│  • 写 → 只能 Primary                                      │
│  • 读 → 可以配置 readPreference                           │
│    - primary(默认)                                       │
│    - primaryPreferred                                      │
│    - secondary(读负载均衡,但可能有延迟)                  │
│    - nearest(就近节点)                                    │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

5.2 分片集群(Sharding) ​

┌─────────────────────────────────────────────────────────────┐
│                    MongoDB 分片集群架构                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────────────────────────────────────────────┐  │
│  │              mongos(路由节点)                       │  │
│  │         客户端连接 mongos,自动路由到分片              │  │
│  └──────────────────────┬───────────────────────────────┘  │
│                         │                                    │
│            ┌────────────┼────────────┐                    │
│            ▼            ▼            ▼                    │
│     ┌──────────┐  ┌──────────┐  ┌──────────┐           │
│     │ Shard 1  │  │ Shard 2  │  │ Shard 3  │           │
│     │ (chunks  │  │ (chunks  │  │ (chunks  │           │
│     │  [0,25)) │  │ [25,50)) │  │ [50,max])│           │
│     └─────┬────┘  └─────┬────┘  └─────┬────┘           │
│           │             │             │                    │
│      ┌────┴────┐   ┌────┴────┐   ┌────┴────┐          │
│      │RS成员1  │   │RS成员1  │   │RS成员1  │          │
│      │RS成员2  │   │RS成员2  │   │RS成员2  │          │
│      └─────────┘   └─────────┘   └─────────┘          │
│                                                             │
│  config server(配置服务器):存储元数据(分片信息)         │
│                                                             │
│  分片键选择原则:                                          │
│  • 高基数(不同值多)                                       │
│  • 非单调递增(避免热点)                                   │
│  • 常用于查询                                               │
│  例:user_id(高基数)、created_at(非单调)               │
│  避免:ObjectId(单调递增)、timestamp(单调递增)          │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

第6部分:mongoose ODM ​

javascript
// mongoose: MongoDB 的对象模型工具
const mongoose = require('mongoose');

// 定义 Schema
const userSchema = new mongoose.Schema({
    name: {
        type: String,
        required: true,
        trim: true,
        index: true
    },
    email: {
        type: String,
        required: true,
        unique: true,
        lowercase: true
    },
    age: {
        type: Number,
        min: 0,
        max: 150
    },
    tags: [String],  // 字符串数组
    orders: [{
        orderId: String,
        total: Number,
        items: [{
            name: String,
            qty: Number,
            price: Number
        }]
    }],
    profile: {
        bio: String,
        website: String
    }
}, {
    timestamps: true  // 自动添加 createdAt 和 updatedAt
});

// 添加实例方法
userSchema.methods.getFullName = function() {
    return `${this.name} (${this.email})`;
};

// 添加静态方法
userSchema.statics.findByEmail = function(email) {
    return this.findOne({ email: email.toLowerCase() });
};

// 添加中间件(钩子)
userSchema.pre('save', function(next) {
    console.log(`Saving user: ${this.name}`);
    next();
});

// 创建 Model
const User = mongoose.model('User', userSchema);

// 实战:关联查询
const Order = mongoose.model('Order', new mongoose.Schema({
    user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
    items: [{ name: String, price: Number }],
    total: Number
}));

// populate(填充关联)
const orders = await Order.find()
    .populate('user', 'name email')
    .sort({ createdAt: -1 });
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

第7部分:MongoDB 的适用与不适用场景 ​

┌─────────────────────────────────────────────────────────────┐
│                 MongoDB 适用 vs 不适用场景                     │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  适用场景:                                                │
│  ✅ 内容管理系统(CMS)、博客、论坛                          │
│  ✅ 用户生成内容(UGC)—— 帖子、评论、动态                 │
│  ✅ 实时分析/事件日志—— 结构随时变化                       │
│  ✅ 产品目录—— 商品属性差异大(服装 vs 电子产品)           │
│  ✅ 物联网(IoT)—— 传感器数据,字段不固定                  │
│  ✅ 快速原型开发—— Schema 变化频繁                         │
│                                                             │
│  不适用场景:                                              │
│  ❌ 强事务场景—— 银行转账(用 PostgreSQL / TiDB)          │
│  ❌ 强关联场景—— 多表 JOIN(用 MySQL / PostgreSQL)        │
│  ❌ 复杂分析—— 跨文档聚合(用 ClickHouse / PostgreSQL)    │
│  ❌ 需要 SQL 的场景—— 团队只会 SQL                         │
│  ❌ 小文件存储—— GridFS 性能不如对象存储                   │
│                                                             │
│  与 Agent/LLM 的关系:                                     │
│  • MongoDB 在 AI 应用中常用于:消息日志、用户行为数据        │
│  • 但核心业务数据(用户配置、Agent 定义)用 PostgreSQL      │
│  • 灵活 Schema 让 MongoDB 适合存储多变的 Agent 会话数据     │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇4. SQLite 嵌入式——零配置的极致轻量 / SQLite as a Lightweight Embedded Database
下一篇6. Redis 全方位——不只是缓存 / Redis Beyond Caching

持续记录,持续成长

Copyright © Tidenflow