目录
一、概念
二、架构
2.1 逻辑结构
2.2 数据模型
2.3 存储引擎:WiredTiger
三、事务
一、概念
MongoDB是文档数据库,基本存储单元是 文档(Document),以BSON格式(一种类json的二进制形式)存储,文档可以包含键值对、嵌套文档、数组等复杂结构,字段可以动态扩展,需要在代码层管理数据约束,适合存储非结构化数据或半结构化数据(如日志、IoT 传感器数据、实时分析)。
文档结构示例:
{"_id": ObjectId("507f191e810c19729de860ea"), // MongoDB 自动生成的唯一主键"name": "Alice Smith","age": 30,"email": "alice@example.com","address": {"street": "123 Main St","city": "Anytown","zip": "12345"},"hobbies": ["reading", "hiking", "photography"],"created_at": ISODate("2023-10-27T10:00:00Z")
}
设计目标:是解决关系型数据库在处理海量数据、高并发、灵活数据结构时遇到的扩展性、性能瓶颈和模式僵化问题。
MongoDB与关系型数据库RDBMS对比:
文档 (Document): 数据存储和操作的基本单元,类似于 JSON 对象,结构是键值对。
集合 (Collection):一组相关文档的容器,同一个集合(Collection)中的文档不要求具有相同的结构(模式自由/动态模式)。字段可以动态添加、修改或删除。
数据库 (Database):包含多个集合的物理容器,用于逻辑上隔离不同的应用或数据集,每个数据库有自己的用户、权限、集合和索引。
MongoDB中Document中可以出现的数据类型:
二、架构
2.1 逻辑结构
MongoDB与MySQL架构相差不多,底层都使⽤了可插拔的存储引擎以满用户的不同需要。最新版本的 MongoDB 中使用了 WiredTiger 作为默认的存储引擎,WiredTiger 提供了不同粒度的并发控制和压缩机制,能够为不同种类的应用提供了最好的性能和存储率。
在存储引擎上层的就是 MongoDB 的数据模型和查询语言了,由于 MongoDB 对数据的存储与 RDBMS 有较大的差异,所以它创建了一套不同的数据模型和查询语言。
2.2 数据模型
MongoDB的数据模型有两种
1> 内嵌:把相关联的数据保存在同一个文档结构中
-
适用场景:一对一或一对少关系,高频查询的子数据。
-
示例:用户档案中直接嵌套地址信息
{"user_id": 1001,"profile": {"birthdate": "1990-05-15","education": "硕士"}
}
-
优点:单次查询获取全部数据,读写高效。
-
缺点:文档大小可能膨胀(上限 16MB)。
2> 引用:通过存储数据引用信息来实现两个不同文档之间的关联
-
适用场景:一对多或多对多关系,子数据独立更新频繁。
-
示例:用户与订单通过
_id
关联
// users 集合
{ "_id": ObjectId("662a1b87c1b6e32a50f1a9e2"), "name": "张三","email": "zhangsan@example.com"
}// orders 集合
{ "_id": ObjectId("55f14312c7447c3da7051b27"),"user_id": ObjectId("662a1b87c1b6e32a50f1a9e2"), // 引用用户ID"items": ["手机", "耳机"],"total": 5999
}
// 关联查询:获取所有订单及其对应的用户信息
db.orders.aggregate([{$lookup: {from: "users", // 关联的集合名localField: "user_id", // 当前集合的关联字段foreignField: "_id", // 目标集合的关联字段as: "user_info" // 输出字段名(数组)}}
])// 输出结果
{"_id": ObjectId("55f14312c7447c3da7051b27"),"user_id": ObjectId("662a1b87c1b6e32a50f1a9e2"),"items": ["手机", "耳机"],"total": 5999,"user_info": [ // 注意:结果总是数组{"_id": ObjectId("662a1b87c1b6e32a50f1a9e2"),"name": "张三","email": "zhangsan@example.com"}]
}
-
优点:数据冗余少,易于维护一致性。
-
缺点:需多次查询(
$lookup
聚合操作可缓解)。
2.3 存储引擎:WiredTiger
存储引擎负责管理如何在磁盘上存储数据、处理读写操作、实现事务、管理内存缓存等。
MongoDB支持的存储引擎有MMAPv1,WiredTiger和InMemory。InMemory存储引擎用于将数据只存储在内存中,只将少量的元数据(meta-data)和诊断日志(Diagnostic)存储到硬盘文件中,由于不需要Disk的IO操作,就能获取所需的数据,InMemory存储引擎大幅度降低了数据查询的延迟(Latency)。从mongodb3.2开始默认的存储引擎是WiredTiger,3.2版本之前的默认存储引擎是MMAPv1,mongodb4.x版本不再支持MMAPv1存储引擎。
核心原理:
-
文档级并发控制: 这是 WiredTiger 高性能的关键。它允许对集合中的不同文档进行并发读写操作(写操作在文档级别加锁),极大地提高了多核CPU环境下的吞吐量。相比之前的 MMAPv1 引擎(集合级锁),这是质的飞跃。
-
写操作流程
-
数据写入 Journal 预写日志(防崩溃)
-
更新内存中的 Cache
-
后台线程异步刷盘:
-
每 60 秒生成 Checkpoint
-
压缩后写入数据文件
-
-
-
写优化与持久性:
-
Write Ahead Logging (WAL): 所有写操作首先被顺序、快速地写入一个持久化的 Journal 文件(并默认写入Cache)。这确保了即使发生崩溃,也能根据 Journal 恢复未刷盘的数据,保证操作的持久性(Durability)。
-
内存管理: 使用缓存(Cache) 来存储频繁访问的数据和索引(通过 LRU 算法管理)。写操作先在内存的 Cache 中进行修改,然后异步写入 Journal 和最终刷入数据文件。读操作优先从 Cache 读取。
-
Checkpoints: WiredTiger 定期(默认 60 秒或 Journal 达到 2GB)将内存中修改过的数据(脏页)以一致的状态快照写入数据文件。Checkpoint 是数据的持久化点,缩短了崩溃恢复时需要从 Journal 重放的操作量。
-
-
压缩: WiredTiger 支持对数据(Snappy 或 Zlib)和索引(Prefix)进行压缩,显著节省磁盘空间并减少 I/O。
-
B-Tree 索引存储: WiredTiger 使用 B-Tree 数据结构存储索引(这是 MongoDB 索引的主要实现方式)。
-
WiredTiger 关键配置 (
mongod.conf
)
storage:engine: wiredTigerwiredTiger:engineConfig:cacheSizeGB: 10 # 内存缓存大小 (建议为物理内存50%)journalCompressor: snappy # 日志压缩算法collectionConfig:blockCompressor: zstd # 数据压缩算法 (zstd平衡性能/压缩比)indexConfig:prefixCompression: true # 索引前缀压缩
三、事务
-
支持范围: MongoDB 4.0+ 支持多文档事务(ACID),适用于副本集;MongoDB 4.2+ 支持分布式事务(跨分片事务)。
-
实现原理:
-
使用快照隔离 (Snapshot Isolation) 级别。事务看到的是事务开始时数据库的一致性快照。
-
在 WiredTiger 存储引擎中,事务通过 MVCC (多版本并发控制) 实现。写操作在事务提交前对其他事务不可见。
-
涉及跨分片事务时,由协调者(通常是发起事务的 Mongos 或 Mongod)使用 Two-Phase Commit (2PC) 协议协调所有参与分片。
-
-
注意事项: 虽然提供了 ACID 保证,但分布式事务比单文档操作开销大得多,应谨慎使用,避免长时间运行的事务。单文档操作天生具有原子性。
举例:
const session = db.getMongo().startSession(); // 1. 创建会话
session.startTransaction({ // 2. 启动事务readConcern: { level: "snapshot" },writeConcern: { w: "majority" }
});
try {const coll = session.getDatabase("test").users;coll.updateOne({ name: "Alice" }, { $inc: { balance: -100 } }); // 3. 操作1coll.updateOne({ name: "Bob" }, { $inc: { balance: 100 } }); // 4. 操作2session.commitTransaction(); // 5. 提交事务
} catch (error) {session.abortTransaction(); // 6. 出错回滚
}