739 字
2 分钟
为什么 MongoDB 使用 B 树
MySQL 使用 B+ 树作为索引结构,而 MongoDB 使用 B 树(准确的说是 B 树的变体)。同样是数据库,为什么选择不同的索引结构?
一、B 树 vs B+ 树回顾
1.1 B 树结构
flowchart TB
R[根节点] --> A[15]
R --> B[50]
R --> C[80]
A --> A1[5, 10]
A --> A2[12, 14]
B --> B1[20, 30]
B --> B2[40, 45]
C --> C1[60, 70]
C --> C2[90, 100]
style R fill:#f96
style A fill:#ff9
style B fill:#ff9
style C fill:#ff9
B 树特点:
- 所有节点都存储数据
- 查找可能在任意节点终止
- 非叶子节点也存储实际数据
1.2 B+ 树结构
flowchart TB
subgraph 索引节点
R[15, 30]
end
subgraph 叶子节点(链表)
L1[10] <--> L2[20] <--> L3[30] <--> L4[40] <--> L5[50]
end
R --> L1
R --> L3
R --> L5
style R fill:#f96
style L1 fill:#9f9
style L2 fill:#9f9
style L3 fill:#9f9
style L4 fill:#9f9
style L5 fill:#9f9
B+ 树特点:
- 仅叶子节点存储数据
- 叶子节点链表连接
- 索引节点只存 key
二、MongoDB 的文档模型
2.1 文档数据库的访问模式
MongoDB 是文档数据库,数据以 JSON-like 文档存储:
// MongoDB 文档示例{ "_id": ObjectId("..."), "name": "张三", "age": 28, "address": { "city": "北京", "district": "朝阳区" }, "orders": [ { "id": 1, "amount": 100 }, { "id": 2, "amount": 200 } ]}2.2 内嵌文档 vs 引用
MongoDB 鼓励内嵌文档:
// 内嵌文档(MongoDB 推荐){ "order_id": "1001", "customer": { "name": "张三", "phone": "13800138000" }, "items": [ { "product": "A", "qty": 2 }, { "product": "B", "qty": 1 } ]}
// vs 关系型数据库的多表关联// orders 表 + customers 表 + order_items 表三、B 树在 MongoDB 中的优势
3.1 点查询(Point Query)
// 查询单个文档db.users.findOne({ _id: ObjectId("...") });B 树的查找路径:
flowchart TB
R[根节点<br/>包含数据] --> A[中间节点<br/>包含数据]
A --> L[叶子节点<br/>包含完整文档]
Note: 找到后直接返回,无需二次查找
B 树的优点:查到即可返回,数据就在节点上。
3.2 范围查询
// 范围查询db.orders.find({ amount: { $gte: 100, $lte: 500 } });B+ 树的范围查询:
flowchart LR
L1[10] <--> L2[20] <--> L3[30] <--> L4[40] <--> L5[50]
Note over L1,L5: 链表遍历,顺序访问
B+ 树在范围查询时必须遍历链表,而 B 树可能需要回溯。
3.3 写入性能
| 操作 | B 树 | B+ 树 |
|---|---|---|
| 插入 | 可能分裂任意节点 | 只分裂叶子节点 |
| 删除 | 可能合并任意节点 | 只合并叶子节点 |
| 复杂度 | O(log n) | O(log n) |
B 树的写入可能影响上层节点,而 B+ 树只影响叶子节点。
四、为什么 MySQL 用 B+ 树?
4.1 MySQL 的存储结构
MySQL(InnoDB)将数据和索引分开:
flowchart TB
subgraph 主键索引(B+ 树)
R[根节点<br/>索引]
R --> L1[...]
R --> L2[...]
L1 --> D1[(完整行数据)]
L2 --> D2[(完整行数据)]
end
Note: 索引指向数据行<br/>行数据存储在叶子节点
4.2 MySQL 的场景
MySQL 存储的是结构化行数据:
- 每次查询返回整行
- 叶子节点存储行指针或行数据
- 范围查询需要遍历叶子链表
B+ 树更高效,因为:
- 非叶子节点只存索引,容纳更多 key
- 树更扁平,I/O 更少
- 链表支持高效范围扫描
五、MongoDB 的 WiredTiger 引擎
5.1 WiredTiger 的 B 树
MongoDB 3.0+ 默认使用 WiredTiger 存储引擎:
// WiredTiger 索引结构// B 树变体,针对 SSD 优化5.2 压缩和缓存
WiredTiger 的特点:
- 文档级锁(并发更高)
- 压缩存储(节省空间)
- B 树 + 写时复制
六、对比总结
| 特性 | MongoDB (B 树) | MySQL (B+ 树) |
|---|---|---|
| 数据位置 | 所有节点 | 仅叶子节点 |
| 点查询 | 可能更少 I/O | 稳定 I/O |
| 范围查询 | 可能回溯 | 链表遍历 |
| 模型 | 文档数据库 | 关系数据库 |
| 访问模式 | 内嵌文档,单文档操作 | 多表关联,复杂查询 |
七、核心原因
MongoDB 选择 B 树的原因:
| 原因 | 说明 |
|---|---|
| 文档模型 | 内嵌文档,单文档查询为主 |
| 读取模式 | 点查询多,范围查询相对少 |
| 简单性 | B 树实现更简单 |
| 覆盖查询 | 索引覆盖时直接返回,无需回表 |
本质:不同的数据模型和访问模式,导致了不同的索引选择。
参考资料
- MongoDB Indexes — MongoDB 索引文档
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
为什么 MongoDB 使用 B 树
https://blog.souloss.com/posts/why-the-design/why-mongodb-uses-b-tree/ 部分信息可能已经过时
相关文章 智能推荐
1
为什么 MySQL 使用 B+ 树
技术科普 深入解析 MySQL 为什么选择 B+ 树作为索引结构,对比 B 树、哈希表等其他数据结构,理解数据库索引设计。
2
为什么 PostgreSQL 使用 MVCC
技术科普 深入解析多版本并发控制的设计原理,理解 PostgreSQL 如何实现高并发事务处理。
3
为什么数据库会丢失数据
技术科普 深入解析数据库丢失数据的场景与原因,WAL、fsync、缓冲池等机制与数据安全。
4
为什么数据库不应该使用外键
技术科普 深入解析为什么现代互联网应用中不建议使用外键,以及如何替代外键实现数据一致性。
5
为什么 OLAP 需要列式存储
技术科普 深入解析为什么 OLAP 数据库使用列式存储,以及它相比行式存储的优势。






