mobile wallpaper 1mobile wallpaper 2mobile wallpaper 3mobile wallpaper 4
739 字
2 分钟
为什么 MongoDB 使用 B 树
2023-04-06

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 使用 B 树
https://blog.souloss.com/posts/why-the-design/why-mongodb-uses-b-tree/
作者
Souloss
发布于
2023-04-06
许可协议
CC BY-NC-SA 4.0

部分信息可能已经过时