MongoDB原理

发布于:2025-06-25 ⋅ 阅读:(20) ⋅ 点赞:(0)

目录

一、概念

二、架构

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: wiredTiger
  wiredTiger:
    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. 操作1
  coll.updateOne({ name: "Bob" }, { $inc: { balance: 100 } });    // 4. 操作2
  session.commitTransaction();               // 5. 提交事务
} catch (error) {
  session.abortTransaction();                // 6. 出错回滚
}


网站公告

今日签到

点亮在社区的每一天
去签到